Merge branch 'development' into Atom/guthadam/thumbnail_and_preview_refactor
This commit is contained in:
@@ -23,20 +23,20 @@ TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "tests")
|
||||
class TestAtomEditorComponentsMain(object):
|
||||
"""Holds tests for Atom components."""
|
||||
|
||||
@pytest.mark.test_case_id("C32078118") # Decal
|
||||
@pytest.mark.test_case_id("C32078119") # DepthOfField
|
||||
@pytest.mark.test_case_id("C32078120") # Directional Light
|
||||
@pytest.mark.test_case_id("C32078121") # Exposure Control
|
||||
@pytest.mark.test_case_id("C32078115") # Global Skylight (IBL)
|
||||
@pytest.mark.test_case_id("C32078125") # Physical Sky
|
||||
@pytest.mark.test_case_id("C32078127") # PostFX Layer
|
||||
@pytest.mark.test_case_id("C32078131") # PostFX Radius Weight Modifier
|
||||
@pytest.mark.test_case_id("C32078117") # Light
|
||||
@pytest.mark.test_case_id("C36525660") # Display Mapper
|
||||
def test_AtomEditorComponents_AddedToEntity(self, request, editor, level, workspace, project, launcher_platform):
|
||||
"""
|
||||
Please review the hydra script run by this test for more specific test info.
|
||||
Tests the following Atom components and verifies all "expected_lines" appear in Editor.log:
|
||||
1. Display Mapper
|
||||
2. Light
|
||||
3. PostFX Radius Weight Modifier
|
||||
4. PostFX Layer
|
||||
5. Physical Sky
|
||||
6. Global Skylight (IBL)
|
||||
7. Exposure Control
|
||||
8. Directional Light
|
||||
9. DepthOfField
|
||||
10. Decal
|
||||
Tests the Atom components & verifies all "expected_lines" appear in Editor.log
|
||||
"""
|
||||
cfg_args = [level]
|
||||
|
||||
@@ -69,6 +69,18 @@ class TestAtomEditorComponentsMain(object):
|
||||
"DepthOfField_test: Entity deleted: True",
|
||||
"DepthOfField_test: UNDO entity deletion works: True",
|
||||
"DepthOfField_test: REDO entity deletion works: True",
|
||||
# Directional Light Component
|
||||
"Directional Light Entity successfully created",
|
||||
"Directional Light_test: Component added to the entity: True",
|
||||
"Directional Light_test: Component removed after UNDO: True",
|
||||
"Directional Light_test: Component added after REDO: True",
|
||||
"Directional Light_test: Entered game mode: True",
|
||||
"Directional Light_test: Exit game mode: True",
|
||||
"Directional Light_test: Entity is hidden: True",
|
||||
"Directional Light_test: Entity is shown: True",
|
||||
"Directional Light_test: Entity deleted: True",
|
||||
"Directional Light_test: UNDO entity deletion works: True",
|
||||
"Directional Light_test: REDO entity deletion works: True",
|
||||
# Exposure Control Component
|
||||
"Exposure Control Entity successfully created",
|
||||
"Exposure Control_test: Component added to the entity: True",
|
||||
@@ -180,6 +192,7 @@ class TestAtomEditorComponentsMain(object):
|
||||
cfg_args=cfg_args,
|
||||
)
|
||||
|
||||
@pytest.mark.test_case_id("C34525095")
|
||||
def test_AtomEditorComponents_LightComponent(
|
||||
self, request, editor, workspace, project, launcher_platform, level):
|
||||
"""
|
||||
@@ -266,6 +279,15 @@ class TestMaterialEditorBasicTests(object):
|
||||
request.addfinalizer(teardown)
|
||||
|
||||
@pytest.mark.parametrize("exe_file_name", ["MaterialEditor"])
|
||||
@pytest.mark.test_case_id("C34448113") # Creating a New Asset.
|
||||
@pytest.mark.test_case_id("C34448114") # Opening an Existing Asset.
|
||||
@pytest.mark.test_case_id("C34448115") # Closing Selected Material.
|
||||
@pytest.mark.test_case_id("C34448116") # Closing All Materials.
|
||||
@pytest.mark.test_case_id("C34448117") # Closing all but Selected Material.
|
||||
@pytest.mark.test_case_id("C34448118") # Saving Material.
|
||||
@pytest.mark.test_case_id("C34448119") # Saving as a New Material.
|
||||
@pytest.mark.test_case_id("C34448120") # Saving as a Child Material.
|
||||
@pytest.mark.test_case_id("C34448121") # Saving all Open Materials.
|
||||
def test_MaterialEditorBasicTests(
|
||||
self, request, workspace, project, launcher_platform, generic_launcher, exe_file_name):
|
||||
|
||||
|
||||
@@ -73,6 +73,7 @@ def create_screenshots_archive(screenshot_path):
|
||||
class TestAllComponentsIndepthTests(object):
|
||||
|
||||
@pytest.mark.parametrize("screenshot_name", ["AtomBasicLevelSetup.ppm"])
|
||||
@pytest.mark.test_case_id("C34603773")
|
||||
def test_BasicLevelSetup_SetsUpLevel(
|
||||
self, request, editor, workspace, project, launcher_platform, level, screenshot_name):
|
||||
"""
|
||||
@@ -115,6 +116,7 @@ class TestAllComponentsIndepthTests(object):
|
||||
|
||||
create_screenshots_archive(screenshot_directory)
|
||||
|
||||
@pytest.mark.test_case_id("C34525095")
|
||||
def test_LightComponent_ScreenshotMatchesGoldenImage(
|
||||
self, request, editor, workspace, project, launcher_platform, level):
|
||||
"""
|
||||
@@ -225,6 +227,8 @@ class TestMaterialEditor(object):
|
||||
pytest.param("-rhi=Vulkan", ["Registering vulkan RHI"])
|
||||
])
|
||||
@pytest.mark.parametrize("exe_file_name", ["MaterialEditor"])
|
||||
@pytest.mark.test_case_id("C30973986") # Material Editor Launching in Dx12
|
||||
@pytest.mark.test_case_id("C30973987") # Material Editor Launching in Vulkan
|
||||
def test_MaterialEditorLaunch_AllRHIOptionsSucceed(
|
||||
self, request, workspace, project, launcher_platform, generic_launcher, exe_file_name, cfg_args,
|
||||
expected_lines):
|
||||
|
||||
@@ -23,6 +23,7 @@ class TestAutomation(EditorTestSuite):
|
||||
# Remove -autotest_mode from global_extra_cmdline_args since we need rendering for these tests.
|
||||
global_extra_cmdline_args = ["-BatchMode"] # Default is ["-BatchMode", "-autotest_mode"]
|
||||
|
||||
@pytest.mark.test_case_id("C34603773")
|
||||
class AtomGPU_BasicLevelSetup_SetsUpLevel(EditorSharedTest):
|
||||
use_null_renderer = False # Default is True
|
||||
screenshot_name = "AtomBasicLevelSetup.ppm"
|
||||
|
||||
@@ -30,6 +30,7 @@ class TestAtomEditorComponentsSandbox(object):
|
||||
class TestAtomEditorComponentsMain(object):
|
||||
"""Holds tests for Atom components."""
|
||||
|
||||
@pytest.mark.test_case_id("C32078128")
|
||||
def test_AtomEditorComponents_ReflectionProbeAddedToEntity(
|
||||
self, request, editor, level, workspace, project, launcher_platform):
|
||||
"""
|
||||
|
||||
+3
-11
@@ -52,9 +52,6 @@ def Joints_BallLeadFollowerCollide():
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
from JointsHelper import JointEntityCollisionAware
|
||||
|
||||
# Helper Entity class - self.collided flag is set when instance receives collision event.
|
||||
@@ -75,17 +72,12 @@ def Joints_BallLeadFollowerCollide():
|
||||
lead = Entity("lead")
|
||||
follower = Entity("follower")
|
||||
|
||||
# 4) Wait for several seconds
|
||||
general.idle_wait(2.0) # wait for lead and follower to move
|
||||
# 4) Wait for collision between lead and follower or timeout
|
||||
Report.critical_result(Tests.check_collision_happened, helper.wait_for_condition(lambda: lead.collided and follower.collided, 10.0))
|
||||
|
||||
# 5) Check to see if lead and follower behaved as expected
|
||||
Report.critical_result(Tests.check_collision_happened, lead.collided and follower.collided)
|
||||
|
||||
# 6) Exit Game Mode
|
||||
# 5) Exit Game Mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(Joints_BallLeadFollowerCollide)
|
||||
|
||||
+49
-10
@@ -52,11 +52,13 @@ def Joints_HingeNoLimitsConstrained():
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import math
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
import azlmbr.math as azmath
|
||||
|
||||
import JointsHelper
|
||||
from JointsHelper import JointEntity
|
||||
@@ -84,28 +86,65 @@ def Joints_HingeNoLimitsConstrained():
|
||||
Report.info_vector3(lead.position, "lead initial position:")
|
||||
Report.info_vector3(follower.position, "follower initial position:")
|
||||
leadInitialPosition = lead.position
|
||||
followerInitialPosition = follower.position
|
||||
|
||||
# 4) Wait for several seconds
|
||||
general.idle_wait(4.0) # wait for lead and follower to move
|
||||
# 4) Wait for the follower to move above and over the lead or Timeout
|
||||
normalizedStartPos = JointsHelper.getRelativeVector(lead.position, follower.position)
|
||||
normalizedStartPos = normalizedStartPos.GetNormalizedSafe()
|
||||
|
||||
class WaitCondition:
|
||||
ANGLE_CHECKPOINT_1 = math.radians(90)
|
||||
ANGLE_CHECKPOINT_2 = math.radians(200)
|
||||
|
||||
angleAchieved = 0.0
|
||||
followerMovedAbove90Deg = False #this is expected to be true to pass the test
|
||||
followerMovedAbove200Deg = False #this is expected to be true to pass the test
|
||||
|
||||
jointNormal = azmath.Vector3(0.0, -1.0, 0.0) # the joint rotates around the y axis
|
||||
def checkConditionMet(self):
|
||||
#calculate the current follower-lead vector
|
||||
normalVec = JointsHelper.getRelativeVector(lead.position, follower.position)
|
||||
normalVec = normalVec.GetNormalizedSafe()
|
||||
|
||||
#triple product and get the angle
|
||||
tripleProduct = normalizedStartPos.dot(normalVec.cross(self.jointNormal))
|
||||
currentAngle = math.acos(normalizedStartPos.Dot(normalVec))
|
||||
if tripleProduct < 0:
|
||||
currentAngle = (2*math.pi) - currentAngle
|
||||
|
||||
#if the angle is now less then last time, it is no longer rising, so end the test.
|
||||
if currentAngle < self.angleAchieved:
|
||||
return True
|
||||
|
||||
#once we're passed the final check point, end the test
|
||||
if currentAngle > self.ANGLE_CHECKPOINT_2:
|
||||
self.followerMovedAbove200Deg = True
|
||||
return True
|
||||
|
||||
self.angleAchieved = currentAngle
|
||||
self.followerMovedAbove90Deg = currentAngle > self.ANGLE_CHECKPOINT_1
|
||||
return False
|
||||
|
||||
def isFollowerPositionCorrect(self):
|
||||
return self.followerMovedAbove90Deg and self.followerMovedAbove200Deg
|
||||
|
||||
waitCondition = WaitCondition()
|
||||
|
||||
MAX_WAIT_TIME = 10.0 #seconds
|
||||
conditionMet = helper.wait_for_condition(lambda: waitCondition.checkConditionMet(), MAX_WAIT_TIME)
|
||||
|
||||
# 5) Check to see if lead and follower behaved as expected
|
||||
Report.info_vector3(lead.position, "lead position after 1 second:")
|
||||
Report.info_vector3(follower.position, "follower position after 1 second:")
|
||||
Report.info_vector3(lead.position, "lead position after test run:")
|
||||
Report.info_vector3(follower.position, "follower position after test run:")
|
||||
|
||||
leadPositionDelta = lead.position.Subtract(leadInitialPosition)
|
||||
leadRemainedStill = JointsHelper.vector3SmallerThanScalar(leadPositionDelta, FLOAT_EPSILON)
|
||||
Report.critical_result(Tests.check_lead_position, leadRemainedStill)
|
||||
|
||||
followerSwingedOverLead = (follower.position.x < leadInitialPosition.x and
|
||||
follower.position.z > leadInitialPosition.z)
|
||||
Report.critical_result(Tests.check_follower_position, followerSwingedOverLead)
|
||||
Report.critical_result(Tests.check_follower_position, conditionMet and waitCondition.isFollowerPositionCorrect())
|
||||
|
||||
# 6) Exit Game Mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(Joints_HingeNoLimitsConstrained)
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:817bd8dd22e185136418b60fba5b3552993687515d3d5ae96791f2c3be907b92
|
||||
size 7233
|
||||
oid sha256:60276c07b45a734e4f71d695278167ea61e884f8b513906168c9642078ad5954
|
||||
size 6045
|
||||
|
||||
@@ -102,7 +102,7 @@ ly_add_target(
|
||||
3rdParty::Qt::Gui
|
||||
3rdParty::Qt::Widgets
|
||||
3rdParty::Qt::Concurrent
|
||||
3rdParty::tiff
|
||||
3rdParty::TIFF
|
||||
3rdParty::squish-ccr
|
||||
3rdParty::AWSNativeSDK::STS
|
||||
Legacy::CryCommon
|
||||
|
||||
@@ -77,9 +77,11 @@ namespace AZ
|
||||
public:
|
||||
/**
|
||||
* Allocator used by the EBus.
|
||||
* The default setting is AZStd::allocator, which uses AZ::SystemAllocator.
|
||||
* The default setting is Internal EBusEnvironmentAllocator
|
||||
* EBus code stores their Context instances in static memory
|
||||
* Therfore the configured allocator must last as long as the EBus in a module
|
||||
*/
|
||||
using AllocatorType = AZStd::allocator;
|
||||
using AllocatorType = AZ::Internal::EBusEnvironmentAllocator;
|
||||
|
||||
/**
|
||||
* Defines how many handlers can connect to an address on the EBus
|
||||
|
||||
@@ -34,7 +34,9 @@ namespace AZ
|
||||
friend IAllocator;
|
||||
friend class AllocatorBase;
|
||||
friend class Debug::AllocationRecords;
|
||||
friend class AZ::Internal::EnvironmentVariableHolder<AllocatorManager>;
|
||||
template<typename T, typename... Args> friend constexpr auto AZStd::construct_at(T*, Args&&... args)
|
||||
->AZStd::enable_if_t<AZStd::is_void_v<AZStd::void_t<decltype(new (AZStd::declval<void*>()) T(AZStd::forward<Args>(args)...))>>, T*>;
|
||||
template<typename T> constexpr friend void AZStd::destroy_at(T*);
|
||||
|
||||
public:
|
||||
typedef AZStd::function<void (IAllocator* allocator, size_t /*byteSize*/, size_t /*alignment*/, int/* flags*/, const char* /*name*/, const char* /*fileName*/, int lineNum /*=0*/)> OutOfMemoryCBType;
|
||||
|
||||
@@ -251,16 +251,15 @@ namespace AZ
|
||||
class EnvironmentVariableHolder
|
||||
: public EnvironmentVariableHolderBase
|
||||
{
|
||||
void ConstructImpl(const AZStd::true_type& /* AZStd::has_trivial_constructor<T> */)
|
||||
{
|
||||
memset(&m_value, 0, sizeof(T));
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
void ConstructImpl(const AZStd::false_type& /* AZStd::has_trivial_constructor<T> */, Args&&... args)
|
||||
void ConstructImpl(Args&&... args)
|
||||
{
|
||||
// Construction of non-trivial types is left up to the type's constructor.
|
||||
new(&m_value) T(AZStd::forward<Args>(args)...);
|
||||
// Use std::launder to ensure that the compiler treats the T* reinterpret_cast as a new object
|
||||
#if __cpp_lib_launder
|
||||
AZStd::construct_at(std::launder(reinterpret_cast<T*>(&m_value)), AZStd::forward<Args>(args)...);
|
||||
#else
|
||||
AZStd::construct_at(reinterpret_cast<T*>(&m_value), AZStd::forward<Args>(args)...);
|
||||
#endif
|
||||
}
|
||||
static void DestructDispatchNoLock(EnvironmentVariableHolderBase *base, DestroyTarget selfDestruct)
|
||||
{
|
||||
@@ -274,10 +273,12 @@ namespace AZ
|
||||
AZ_Assert(self->m_isConstructed, "Variable is not constructed. Please check your logic and guard if needed!");
|
||||
self->m_isConstructed = false;
|
||||
self->m_moduleOwner = nullptr;
|
||||
if constexpr(!AZStd::is_trivially_destructible_v<T>)
|
||||
{
|
||||
reinterpret_cast<T*>(&self->m_value)->~T();
|
||||
}
|
||||
// Use std::launder to ensure that the compiler treats the T* reinterpret_cast as a new object
|
||||
#if __cpp_lib_launder
|
||||
AZStd::destroy_at(std::launder(reinterpret_cast<T*>(&self->m_value)));
|
||||
#else
|
||||
AZStd::destroy_at(reinterpret_cast<T*>(&self->m_value));
|
||||
#endif
|
||||
}
|
||||
public:
|
||||
EnvironmentVariableHolder(u32 guid, bool isOwnershipTransfer, Environment::AllocatorInterface* allocator)
|
||||
@@ -303,24 +304,13 @@ namespace AZ
|
||||
UnregisterAndDestroy(DestructDispatchNoLock, moduleRelease);
|
||||
}
|
||||
|
||||
void Construct()
|
||||
{
|
||||
AZStd::lock_guard<AZStd::spin_mutex> lock(m_mutex);
|
||||
if (!m_isConstructed)
|
||||
{
|
||||
ConstructImpl(AZStd::is_trivially_constructible<T>{});
|
||||
m_isConstructed = true;
|
||||
m_moduleOwner = Environment::GetModuleId();
|
||||
}
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
void Construct(Args&&... args)
|
||||
{
|
||||
AZStd::lock_guard<AZStd::spin_mutex> lock(m_mutex);
|
||||
if (!m_isConstructed)
|
||||
{
|
||||
ConstructImpl(typename AZStd::false_type(), AZStd::forward<Args>(args)...);
|
||||
ConstructImpl(AZStd::forward<Args>(args)...);
|
||||
m_isConstructed = true;
|
||||
m_moduleOwner = Environment::GetModuleId();
|
||||
}
|
||||
@@ -333,7 +323,7 @@ namespace AZ
|
||||
}
|
||||
|
||||
// variable storage
|
||||
typename AZStd::aligned_storage<sizeof(T), AZStd::alignment_of<T>::value>::type m_value;
|
||||
AZStd::aligned_storage_for_t<T> m_value;
|
||||
static int s_moduleUseCount;
|
||||
};
|
||||
|
||||
@@ -468,6 +458,11 @@ namespace AZ
|
||||
Get() = value;
|
||||
}
|
||||
|
||||
void Set(T&& value)
|
||||
{
|
||||
Get() = AZStd::move(value);
|
||||
}
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return IsValid();
|
||||
|
||||
@@ -42,7 +42,10 @@ namespace AZ
|
||||
AZ_Assert(m_useCount > 0, "m_useCount is already 0!");
|
||||
if (m_useCount.fetch_sub(1) == 1)
|
||||
{
|
||||
AZ::NameDictionary::Instance().TryReleaseName(hash);
|
||||
if (AZ::NameDictionary::IsReady())
|
||||
{
|
||||
AZ::NameDictionary::Instance().TryReleaseName(hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,23 +21,18 @@ namespace AZ
|
||||
|
||||
namespace NameDictionaryInternal
|
||||
{
|
||||
static AZ::EnvironmentVariable<NameDictionary*> s_instance = nullptr;
|
||||
static AZ::EnvironmentVariable<NameDictionary> s_instance = nullptr;
|
||||
}
|
||||
|
||||
void NameDictionary::Create()
|
||||
{
|
||||
using namespace NameDictionaryInternal;
|
||||
|
||||
AZ_Assert(!s_instance || !s_instance.Get(), "NameDictionary already created!");
|
||||
AZ_Assert(!s_instance, "NameDictionary already created!");
|
||||
|
||||
if (!s_instance)
|
||||
{
|
||||
s_instance = AZ::Environment::CreateVariable<NameDictionary*>(NameDictionaryInstanceName);
|
||||
}
|
||||
|
||||
if (!s_instance.Get())
|
||||
{
|
||||
s_instance.Set(aznew NameDictionary());
|
||||
s_instance = AZ::Environment::CreateVariable<NameDictionary>(NameDictionaryInstanceName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,8 +41,7 @@ namespace AZ
|
||||
using namespace NameDictionaryInternal;
|
||||
|
||||
AZ_Assert(s_instance, "NameDictionary not created!");
|
||||
delete (*s_instance);
|
||||
*s_instance = nullptr;
|
||||
s_instance.Reset();
|
||||
}
|
||||
|
||||
bool NameDictionary::IsReady()
|
||||
@@ -56,10 +50,10 @@ namespace AZ
|
||||
|
||||
if (!s_instance)
|
||||
{
|
||||
s_instance = Environment::FindVariable<NameDictionary*>(NameDictionaryInstanceName);
|
||||
s_instance = Environment::FindVariable<NameDictionary>(NameDictionaryInstanceName);
|
||||
}
|
||||
|
||||
return s_instance && *s_instance;
|
||||
return s_instance.IsConstructed();
|
||||
}
|
||||
|
||||
NameDictionary& NameDictionary::Instance()
|
||||
@@ -68,12 +62,12 @@ namespace AZ
|
||||
|
||||
if (!s_instance)
|
||||
{
|
||||
s_instance = Environment::FindVariable<NameDictionary*>(NameDictionaryInstanceName);
|
||||
s_instance = Environment::FindVariable<NameDictionary>(NameDictionaryInstanceName);
|
||||
}
|
||||
|
||||
AZ_Assert(s_instance && *s_instance, "NameDictionary has not been initialized yet.");
|
||||
AZ_Assert(s_instance.IsConstructed(), "NameDictionary has not been initialized yet.");
|
||||
|
||||
return *(*s_instance);
|
||||
return *s_instance;
|
||||
}
|
||||
|
||||
NameDictionary::NameDictionary()
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#include <AzCore/Memory/OSAllocator.h>
|
||||
#include <AzCore/Name/Name.h>
|
||||
|
||||
namespace MaterialEditor
|
||||
namespace MaterialEditor
|
||||
{
|
||||
class MaterialEditorCoreComponent;
|
||||
}
|
||||
@@ -34,14 +34,14 @@ namespace AZ
|
||||
{
|
||||
class NameData;
|
||||
};
|
||||
|
||||
|
||||
//! Maintains a list of unique strings for Name objects.
|
||||
//! The main benefit of the Name system is very fast string equality comparison, because every
|
||||
//! unique name has a unique ID. The NameDictionary's purpose is to guarantee name IDs do not
|
||||
//! unique name has a unique ID. The NameDictionary's purpose is to guarantee name IDs do not
|
||||
//! collide. It also saves memory by removing duplicate strings.
|
||||
//!
|
||||
//! Benchmarks have shown that creating a new Name object can be quite slow when the name doesn't
|
||||
//! already exist in the NameDictionary, but is comparable to creating an AZStd::string for names
|
||||
//! Benchmarks have shown that creating a new Name object can be quite slow when the name doesn't
|
||||
//! already exist in the NameDictionary, but is comparable to creating an AZStd::string for names
|
||||
//! that already exist.
|
||||
class NameDictionary final
|
||||
{
|
||||
@@ -51,7 +51,10 @@ namespace AZ
|
||||
friend Name;
|
||||
friend Internal::NameData;
|
||||
friend UnitTest::NameDictionaryTester;
|
||||
|
||||
template<typename T, typename... Args> friend constexpr auto AZStd::construct_at(T*, Args&&... args)
|
||||
-> AZStd::enable_if_t<AZStd::is_void_v<AZStd::void_t<decltype(new (AZStd::declval<void*>()) T(AZStd::forward<Args>(args)...))>>, T*>;
|
||||
template<typename T> constexpr friend void AZStd::destroy_at(T*);
|
||||
|
||||
public:
|
||||
|
||||
static void Create();
|
||||
@@ -62,7 +65,7 @@ namespace AZ
|
||||
|
||||
//! Makes a Name from the provided raw string. If an entry already exists in the dictionary, it is shared.
|
||||
//! Otherwise, it is added to the internal dictionary.
|
||||
//!
|
||||
//!
|
||||
//! @param name The name to resolve against the dictionary.
|
||||
//! @return A Name instance holding a dictionary entry associated with the provided raw string.
|
||||
Name MakeName(AZStd::string_view name);
|
||||
@@ -84,13 +87,13 @@ namespace AZ
|
||||
// Attempts to release the name from the dictionary, but checks to make sure
|
||||
// a reference wasn't taken by another thread.
|
||||
void TryReleaseName(Name::Hash hash);
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Calculates a hash for the provided name string.
|
||||
// Does not attempt to resolve hash collisions; that is handled elsewhere.
|
||||
Name::Hash CalcHash(AZStd::string_view name);
|
||||
|
||||
|
||||
AZStd::unordered_map<Name::Hash, Internal::NameData*> m_dictionary;
|
||||
mutable AZStd::shared_mutex m_sharedMutex;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/std/allocator_stateless.h>
|
||||
#include <AzCore/Memory/OSAllocator.h>
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
stateless_allocator::stateless_allocator(const char* name)
|
||||
: m_name(name) {}
|
||||
|
||||
const char* stateless_allocator::get_name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
void stateless_allocator::set_name(const char* name)
|
||||
{
|
||||
m_name = name;
|
||||
}
|
||||
|
||||
auto stateless_allocator::allocate(size_type byteSize) -> pointer_type
|
||||
{
|
||||
return allocate(byteSize, AZ_DEFAULT_ALIGNMENT, 0);
|
||||
}
|
||||
|
||||
auto stateless_allocator::allocate(size_type byteSize, size_type alignment, int) -> pointer_type
|
||||
{
|
||||
pointer_type address = AZ_OS_MALLOC(byteSize, alignment);
|
||||
|
||||
if (address == nullptr)
|
||||
{
|
||||
AZ_Error("Memory", false, "stateless_allocator ran out of system memory!\n");
|
||||
}
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
void stateless_allocator::deallocate(pointer_type ptr, size_type)
|
||||
{
|
||||
AZ_OS_FREE(ptr);
|
||||
}
|
||||
|
||||
void stateless_allocator::deallocate(pointer_type ptr, size_type, size_type)
|
||||
{
|
||||
AZ_OS_FREE(ptr);
|
||||
}
|
||||
|
||||
auto stateless_allocator::max_size() const -> size_type
|
||||
{
|
||||
return AZ_CORE_MAX_ALLOCATOR_SIZE;
|
||||
}
|
||||
|
||||
stateless_allocator stateless_allocator::select_on_container_copy_construction() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto stateless_allocator::resize(pointer_type, size_type) -> size_type
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool stateless_allocator::is_lock_free()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool stateless_allocator::is_stale_read_allowed()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool stateless_allocator::is_delayed_recycling()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// comparison operators
|
||||
bool operator==(const stateless_allocator&, const stateless_allocator&)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator!=(const stateless_allocator&, const stateless_allocator&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/base.h>
|
||||
#include <AzCore/std/typetraits/integral_constant.h>
|
||||
#include <AzCore/RTTI/TypeInfoSimple.h>
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
class stateless_allocator
|
||||
{
|
||||
public:
|
||||
|
||||
AZ_TYPE_INFO(stateless_allocator, "{E4976C53-0B20-4F39-8D41-0A76F59A7D68}");
|
||||
|
||||
using value_type = uint8_t;
|
||||
using pointer_type = void*;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
using allow_memory_leaks = AZStd::true_type;
|
||||
|
||||
stateless_allocator(const char* name = "AZStd::stateless_allocator");
|
||||
stateless_allocator(const stateless_allocator& rhs) = default;
|
||||
|
||||
stateless_allocator& operator=(const stateless_allocator& rhs) = default;
|
||||
|
||||
const char* get_name() const;
|
||||
void set_name(const char* name);
|
||||
|
||||
pointer_type allocate(size_type byteSize);
|
||||
pointer_type allocate(size_type byteSize, size_type alignment, int flags = 0);
|
||||
void deallocate(pointer_type ptr, size_type alignment);
|
||||
void deallocate(pointer_type ptr, size_type byteSize, size_type alignment);
|
||||
|
||||
// max_size actually returns the true maximum size of a single allocation
|
||||
size_type max_size() const;
|
||||
|
||||
// Returns a copy of the allocator
|
||||
stateless_allocator select_on_container_copy_construction() const;
|
||||
|
||||
//! extensions
|
||||
size_type resize(pointer_type ptr, size_type newSize);
|
||||
|
||||
bool is_lock_free();
|
||||
bool is_stale_read_allowed();
|
||||
bool is_delayed_recycling();
|
||||
|
||||
private:
|
||||
const char* m_name;
|
||||
};
|
||||
|
||||
bool operator==(const stateless_allocator& left, const stateless_allocator& right);
|
||||
bool operator!=(const stateless_allocator& left, const stateless_allocator& right);
|
||||
}
|
||||
@@ -12,6 +12,8 @@ set(FILES
|
||||
allocator.h
|
||||
allocator_ref.h
|
||||
allocator_stack.h
|
||||
allocator_stateless.cpp
|
||||
allocator_stateless.h
|
||||
allocator_static.h
|
||||
allocator_traits.h
|
||||
any.h
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
// alias std::pointer_traits into the AZStd::namespace
|
||||
// alias std::pointer_traits into the AZStd::namespace
|
||||
using std::pointer_traits;
|
||||
|
||||
//! Bring the names of uninitialized_default_construct and
|
||||
@@ -229,7 +229,7 @@ namespace AZStd
|
||||
//! `new (declval<void*>()) T(declval<Args>()...)` is well-formed
|
||||
template <typename T, typename... Args>
|
||||
constexpr auto construct_at(T* ptr, Args&&... args)
|
||||
-> enable_if_t<is_void_v<void_t<decltype(new (declval<void*>()) T(AZStd::forward<Args>(args)...))>>, T*>
|
||||
-> enable_if_t<AZStd::is_void_v<AZStd::void_t<decltype(new (AZStd::declval<void*>()) T(AZStd::forward<Args>(args)...))>>, T*>
|
||||
{
|
||||
return ::new (ptr) T(AZStd::forward<Args>(args)...);
|
||||
}
|
||||
@@ -487,7 +487,7 @@ namespace AZStd
|
||||
{
|
||||
//! Implements the C++17 uninitialized_move function
|
||||
//! The functions accepts two input iterators and an output iterator
|
||||
//! It performs an AZStd::move on each in in the range of the input iterator
|
||||
//! It performs an AZStd::move on each in in the range of the input iterator
|
||||
//! and stores the result in location pointed by the output iterator
|
||||
template <typename InputIt, typename ForwardIt>
|
||||
ForwardIt uninitialized_move(InputIt first, InputIt last, ForwardIt result)
|
||||
|
||||
@@ -29,6 +29,10 @@ namespace AzFramework
|
||||
AZStd::vector<AZ::IO::Path> m_absoluteSourcePaths; //!< Where the gem's source path folder are located(as an absolute path)
|
||||
|
||||
static constexpr const char* GetGemAssetFolder() { return "Assets"; }
|
||||
static constexpr const char* GetGemRegistryFolder()
|
||||
{
|
||||
return "Registry";
|
||||
}
|
||||
};
|
||||
|
||||
//! Returns a list of GemInfo of all the gems that are active for the for the specified game project.
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace AzFramework
|
||||
->Field("terminationTime", &SessionConfig::m_terminationTime)
|
||||
->Field("creatorId", &SessionConfig::m_creatorId)
|
||||
->Field("sessionProperties", &SessionConfig::m_sessionProperties)
|
||||
->Field("matchmakingData", &SessionConfig::m_matchmakingData)
|
||||
->Field("sessionId", &SessionConfig::m_sessionId)
|
||||
->Field("sessionName", &SessionConfig::m_sessionName)
|
||||
->Field("dnsName", &SessionConfig::m_dnsName)
|
||||
@@ -46,6 +47,8 @@ namespace AzFramework
|
||||
"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_matchmakingData,
|
||||
"MatchmakingData", "The matchmaking process information that was used to create the 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,
|
||||
|
||||
@@ -35,6 +35,9 @@ namespace AzFramework
|
||||
|
||||
// A collection of custom properties for a session.
|
||||
AZStd::unordered_map<AZStd::string, AZStd::string> m_sessionProperties;
|
||||
|
||||
// The matchmaking process information that was used to create the session.
|
||||
AZStd::string m_matchmakingData;
|
||||
|
||||
// A unique identifier for the session.
|
||||
AZStd::string m_sessionId;
|
||||
|
||||
@@ -41,6 +41,11 @@ namespace AzFramework
|
||||
// OnDestroySessionBegin is fired at the beginning of session termination
|
||||
// @return The result of all OnDestroySessionBegin notifications
|
||||
virtual bool OnDestroySessionBegin() = 0;
|
||||
|
||||
// OnUpdateSessionBegin is fired at the beginning of session update
|
||||
// @param sessionConfig The properties to describe a session
|
||||
// @param updateReason The reason for session update
|
||||
virtual void OnUpdateSessionBegin(const SessionConfig& sessionConfig, const AZStd::string& updateReason) = 0;
|
||||
};
|
||||
using SessionNotificationBus = AZ::EBus<SessionNotifications>;
|
||||
} // namespace AzFramework
|
||||
|
||||
@@ -96,6 +96,8 @@ namespace AzGameFramework
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(registry, m_commandLine, false);
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_ProjectUserRegistry(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer);
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(registry, m_commandLine, true);
|
||||
#else
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(registry, m_commandLine, false);
|
||||
#endif
|
||||
// Update the Runtime file paths in case the "{BootstrapSettingsRootKey}/assets" key was overriden by a setting registry
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(registry);
|
||||
|
||||
+4
-2
@@ -28,8 +28,10 @@ namespace AzToolsFramework
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//! Triggered when the editor focus is changed to a different entity.
|
||||
//! @param entityId The entity the focus has been moved to.
|
||||
virtual void OnEditorFocusChanged(AZ::EntityId entityId) = 0;
|
||||
//! @param previousFocusEntityId The entity the focus has been moved from.
|
||||
//! @param newFocusEntityId The entity the focus has been moved to.
|
||||
virtual void OnEditorFocusChanged(
|
||||
[[maybe_unused]] AZ::EntityId previousFocusEntityId, [[maybe_unused]] AZ::EntityId newFocusEntityId) {}
|
||||
|
||||
protected:
|
||||
~FocusModeNotifications() = default;
|
||||
|
||||
+2
-1
@@ -71,8 +71,9 @@ namespace AzToolsFramework
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::EntityId previousFocusEntityId = m_focusRoot;
|
||||
m_focusRoot = entityId;
|
||||
FocusModeNotificationBus::Broadcast(&FocusModeNotifications::OnEditorFocusChanged, m_focusRoot);
|
||||
FocusModeNotificationBus::Broadcast(&FocusModeNotifications::OnEditorFocusChanged, previousFocusEntityId, m_focusRoot);
|
||||
|
||||
if (auto tracker = AZ::Interface<ViewportEditorModeTrackerInterface>::Get();
|
||||
tracker != nullptr)
|
||||
|
||||
@@ -8,12 +8,14 @@
|
||||
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusHandler.h>
|
||||
|
||||
#include <AzToolsFramework/Commands/SelectionCommand.h>
|
||||
#include <AzToolsFramework/ContainerEntity/ContainerEntityInterface.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
|
||||
#include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceEntityMapperInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusNotificationBus.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusUndo.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab
|
||||
{
|
||||
@@ -28,10 +30,12 @@ namespace AzToolsFramework::Prefab
|
||||
|
||||
EditorEntityContextNotificationBus::Handler::BusConnect();
|
||||
AZ::Interface<PrefabFocusInterface>::Register(this);
|
||||
AZ::Interface<PrefabFocusPublicInterface>::Register(this);
|
||||
}
|
||||
|
||||
PrefabFocusHandler::~PrefabFocusHandler()
|
||||
{
|
||||
AZ::Interface<PrefabFocusPublicInterface>::Unregister(this);
|
||||
AZ::Interface<PrefabFocusInterface>::Unregister(this);
|
||||
EditorEntityContextNotificationBus::Handler::BusDisconnect();
|
||||
}
|
||||
@@ -61,6 +65,44 @@ namespace AzToolsFramework::Prefab
|
||||
}
|
||||
|
||||
PrefabFocusOperationResult PrefabFocusHandler::FocusOnOwningPrefab(AZ::EntityId entityId)
|
||||
{
|
||||
// Initialize Undo Batch object
|
||||
ScopedUndoBatch undoBatch("Edit Prefab");
|
||||
|
||||
// Clear selection
|
||||
{
|
||||
const EntityIdList selectedEntities = EntityIdList{};
|
||||
auto selectionUndo = aznew SelectionCommand(selectedEntities, "Clear Selection");
|
||||
selectionUndo->SetParent(undoBatch.GetUndoBatch());
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::SetSelectedEntities, selectedEntities);
|
||||
}
|
||||
|
||||
// Edit Prefab
|
||||
{
|
||||
auto editUndo = aznew PrefabFocusUndo("Edit Prefab");
|
||||
editUndo->Capture(entityId);
|
||||
editUndo->SetParent(undoBatch.GetUndoBatch());
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::RunRedoSeparately, editUndo);
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
PrefabFocusOperationResult PrefabFocusHandler::FocusOnPathIndex([[maybe_unused]] AzFramework::EntityContextId entityContextId, int index)
|
||||
{
|
||||
if (index < 0 || index >= m_instanceFocusVector.size())
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Prefab Focus Handler: Invalid index on FocusOnPathIndex."));
|
||||
}
|
||||
|
||||
InstanceOptionalReference focusedInstance = m_instanceFocusVector[index];
|
||||
|
||||
FocusOnOwningPrefab(focusedInstance->get().GetContainerEntityId());
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
PrefabFocusOperationResult PrefabFocusHandler::FocusOnPrefabInstanceOwningEntityId(AZ::EntityId entityId)
|
||||
{
|
||||
InstanceOptionalReference focusedInstance;
|
||||
|
||||
@@ -85,18 +127,6 @@ namespace AzToolsFramework::Prefab
|
||||
return FocusOnPrefabInstance(focusedInstance);
|
||||
}
|
||||
|
||||
PrefabFocusOperationResult PrefabFocusHandler::FocusOnPathIndex([[maybe_unused]] AzFramework::EntityContextId entityContextId, int index)
|
||||
{
|
||||
if (index < 0 || index >= m_instanceFocusVector.size())
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Prefab Focus Handler: Invalid index on FocusOnPathIndex."));
|
||||
}
|
||||
|
||||
InstanceOptionalReference focusedInstance = m_instanceFocusVector[index];
|
||||
|
||||
return FocusOnPrefabInstance(focusedInstance);
|
||||
}
|
||||
|
||||
PrefabFocusOperationResult PrefabFocusHandler::FocusOnPrefabInstance(InstanceOptionalReference focusedInstance)
|
||||
{
|
||||
if (!focusedInstance.has_value())
|
||||
@@ -122,17 +152,10 @@ namespace AzToolsFramework::Prefab
|
||||
if (focusedInstance->get().GetParentInstance() != AZStd::nullopt)
|
||||
{
|
||||
containerEntityId = focusedInstance->get().GetContainerEntityId();
|
||||
|
||||
// Select the container entity
|
||||
AzToolsFramework::SelectEntity(containerEntityId);
|
||||
}
|
||||
else
|
||||
{
|
||||
containerEntityId = AZ::EntityId();
|
||||
|
||||
// Clear the selection
|
||||
AzToolsFramework::SelectEntities({});
|
||||
|
||||
}
|
||||
|
||||
// Focus on the descendants of the container entity
|
||||
@@ -161,6 +184,17 @@ namespace AzToolsFramework::Prefab
|
||||
return m_focusedInstance;
|
||||
}
|
||||
|
||||
AZ::EntityId PrefabFocusHandler::GetFocusedPrefabContainerEntityId([[maybe_unused]] AzFramework::EntityContextId entityContextId) const
|
||||
{
|
||||
if (!m_focusedInstance.has_value())
|
||||
{
|
||||
// PrefabFocusHandler has not been initialized yet.
|
||||
return AZ::EntityId();
|
||||
}
|
||||
|
||||
return m_focusedInstance->get().GetContainerEntityId();
|
||||
}
|
||||
|
||||
bool PrefabFocusHandler::IsOwningPrefabBeingFocused(AZ::EntityId entityId) const
|
||||
{
|
||||
if (!m_focusedInstance.has_value())
|
||||
@@ -200,7 +234,7 @@ namespace AzToolsFramework::Prefab
|
||||
m_instanceFocusVector.clear();
|
||||
|
||||
// Focus on the root prefab (AZ::EntityId() will default to it)
|
||||
FocusOnOwningPrefab(AZ::EntityId());
|
||||
FocusOnPrefabInstanceOwningEntityId(AZ::EntityId());
|
||||
}
|
||||
|
||||
void PrefabFocusHandler::RefreshInstanceFocusList()
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusPublicInterface.h>
|
||||
#include <AzToolsFramework/Prefab/Template/Template.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
@@ -28,6 +29,7 @@ namespace AzToolsFramework::Prefab
|
||||
//! Handles Prefab Focus mode, determining which prefab file entity changes will target.
|
||||
class PrefabFocusHandler final
|
||||
: private PrefabFocusInterface
|
||||
, private PrefabFocusPublicInterface
|
||||
, private EditorEntityContextNotificationBus::Handler
|
||||
{
|
||||
public:
|
||||
@@ -39,10 +41,14 @@ namespace AzToolsFramework::Prefab
|
||||
void Initialize();
|
||||
|
||||
// PrefabFocusInterface overrides ...
|
||||
PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) override;
|
||||
PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) override;
|
||||
PrefabFocusOperationResult FocusOnPrefabInstanceOwningEntityId(AZ::EntityId entityId) override;
|
||||
TemplateId GetFocusedPrefabTemplateId(AzFramework::EntityContextId entityContextId) const override;
|
||||
InstanceOptionalReference GetFocusedPrefabInstance(AzFramework::EntityContextId entityContextId) const override;
|
||||
|
||||
// PrefabFocusPublicInterface overrides ...
|
||||
PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) override;
|
||||
PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) override;
|
||||
AZ::EntityId GetFocusedPrefabContainerEntityId(AzFramework::EntityContextId entityContextId) const override;
|
||||
bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) const override;
|
||||
const AZ::IO::Path& GetPrefabFocusPath(AzFramework::EntityContextId entityContextId) const override;
|
||||
const int GetPrefabFocusPathLength(AzFramework::EntityContextId entityContextId) const override;
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace AzToolsFramework::Prefab
|
||||
{
|
||||
using PrefabFocusOperationResult = AZ::Outcome<void, AZStd::string>;
|
||||
|
||||
//! Interface to handle operations related to the Prefab Focus system.
|
||||
//! Interface to handle internal operations related to the Prefab Focus system.
|
||||
class PrefabFocusInterface
|
||||
{
|
||||
public:
|
||||
@@ -28,29 +28,13 @@ namespace AzToolsFramework::Prefab
|
||||
|
||||
//! Set the focused prefab instance to the owning instance of the entityId provided.
|
||||
//! @param entityId The entityId of the entity whose owning instance we want the prefab system to focus on.
|
||||
virtual PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) = 0;
|
||||
|
||||
//! Set the focused prefab instance to the instance at position index of the current path.
|
||||
//! @param index The index of the instance in the current path that we want the prefab system to focus on.
|
||||
virtual PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) = 0;
|
||||
virtual PrefabFocusOperationResult FocusOnPrefabInstanceOwningEntityId(AZ::EntityId entityId) = 0;
|
||||
|
||||
//! Returns the template id of the instance the prefab system is focusing on.
|
||||
virtual TemplateId GetFocusedPrefabTemplateId(AzFramework::EntityContextId entityContextId) const = 0;
|
||||
|
||||
//! Returns a reference to the instance the prefab system is focusing on.
|
||||
virtual InstanceOptionalReference GetFocusedPrefabInstance(AzFramework::EntityContextId entityContextId) const = 0;
|
||||
|
||||
//! Returns whether the entity belongs to the instance that is being focused on, or one of its descendants.
|
||||
//! @param entityId The entityId of the queried entity.
|
||||
//! @return true if the entity belongs to the focused instance or one of its descendants, false otherwise.
|
||||
virtual bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) const = 0;
|
||||
|
||||
//! Returns the path from the root instance to the currently focused instance.
|
||||
//! @return A path composed from the names of the container entities for the instance path.
|
||||
virtual const AZ::IO::Path& GetPrefabFocusPath(AzFramework::EntityContextId entityContextId) const = 0;
|
||||
|
||||
//! Returns the size of the path to the currently focused instance.
|
||||
virtual const int GetPrefabFocusPathLength(AzFramework::EntityContextId entityContextId) const = 0;
|
||||
};
|
||||
|
||||
} // namespace AzToolsFramework::Prefab
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
#include <AzFramework/Entity/EntityContext.h>
|
||||
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/Template/Template.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab
|
||||
{
|
||||
using PrefabFocusOperationResult = AZ::Outcome<void, AZStd::string>;
|
||||
|
||||
//! Public Interface for external systems to utilize the Prefab Focus system.
|
||||
class PrefabFocusPublicInterface
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(PrefabFocusPublicInterface, "{53EE1D18-A41F-4DB1-9B73-9448F425722E}");
|
||||
|
||||
//! Set the focused prefab instance to the owning instance of the entityId provided. Supports undo/redo.
|
||||
//! @param entityId The entityId of the entity whose owning instance we want the prefab system to focus on.
|
||||
virtual PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) = 0;
|
||||
|
||||
//! Set the focused prefab instance to the instance at position index of the current path. Supports undo/redo.
|
||||
//! @param index The index of the instance in the current path that we want the prefab system to focus on.
|
||||
virtual PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) = 0;
|
||||
|
||||
//! Returns the entity id of the container entity for the instance the prefab system is focusing on.
|
||||
virtual AZ::EntityId GetFocusedPrefabContainerEntityId(AzFramework::EntityContextId entityContextId) const = 0;
|
||||
|
||||
//! Returns whether the entity belongs to the instance that is being focused on, or one of its descendants.
|
||||
//! @param entityId The entityId of the queried entity.
|
||||
//! @return true if the entity belongs to the focused instance or one of its descendants, false otherwise.
|
||||
virtual bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) const = 0;
|
||||
|
||||
//! Returns the path from the root instance to the currently focused instance.
|
||||
//! @return A path composed from the names of the container entities for the instance path.
|
||||
virtual const AZ::IO::Path& GetPrefabFocusPath(AzFramework::EntityContextId entityContextId) const = 0;
|
||||
|
||||
//! Returns the size of the path to the currently focused instance.
|
||||
virtual const int GetPrefabFocusPathLength(AzFramework::EntityContextId entityContextId) const = 0;
|
||||
};
|
||||
|
||||
} // namespace AzToolsFramework::Prefab
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusUndo.h>
|
||||
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusPublicInterface.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab
|
||||
{
|
||||
PrefabFocusUndo::PrefabFocusUndo(const AZStd::string& undoOperationName)
|
||||
: UndoSystem::URSequencePoint(undoOperationName)
|
||||
{
|
||||
m_prefabFocusInterface = AZ::Interface<PrefabFocusInterface>::Get();
|
||||
AZ_Assert(m_prefabFocusInterface, "PrefabFocusUndo - Failed to grab prefab focus interface");
|
||||
|
||||
m_prefabFocusPublicInterface = AZ::Interface<PrefabFocusPublicInterface>::Get();
|
||||
AZ_Assert(m_prefabFocusPublicInterface, "PrefabFocusUndo - Failed to grab prefab focus public interface");
|
||||
}
|
||||
|
||||
bool PrefabFocusUndo::Changed() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void PrefabFocusUndo::Capture(AZ::EntityId entityId)
|
||||
{
|
||||
auto entityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
EditorEntityContextRequestBus::BroadcastResult(entityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
|
||||
|
||||
m_beforeEntityId = m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(entityContextId);
|
||||
m_afterEntityId = entityId;
|
||||
}
|
||||
|
||||
void PrefabFocusUndo::Undo()
|
||||
{
|
||||
m_prefabFocusInterface->FocusOnPrefabInstanceOwningEntityId(m_beforeEntityId);
|
||||
}
|
||||
|
||||
void PrefabFocusUndo::Redo()
|
||||
{
|
||||
m_prefabFocusInterface->FocusOnPrefabInstanceOwningEntityId(m_afterEntityId);
|
||||
}
|
||||
|
||||
} // namespace AzToolsFramework::Prefab
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/EntityId.h>
|
||||
#include <AzToolsFramework/Undo/UndoSystem.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab
|
||||
{
|
||||
class PrefabFocusInterface;
|
||||
class PrefabFocusPublicInterface;
|
||||
|
||||
//! Undo node for prefab focus change operations.
|
||||
class PrefabFocusUndo
|
||||
: public UndoSystem::URSequencePoint
|
||||
{
|
||||
public:
|
||||
explicit PrefabFocusUndo(const AZStd::string& undoOperationName);
|
||||
|
||||
bool Changed() const override;
|
||||
void Capture(AZ::EntityId entityId);
|
||||
|
||||
void Undo() override;
|
||||
void Redo() override;
|
||||
|
||||
protected:
|
||||
PrefabFocusInterface* m_prefabFocusInterface = nullptr;
|
||||
PrefabFocusPublicInterface* m_prefabFocusPublicInterface = nullptr;
|
||||
|
||||
AZ::EntityId m_beforeEntityId;
|
||||
AZ::EntityId m_afterEntityId;
|
||||
};
|
||||
} // namespace AzToolsFramework::Prefab
|
||||
@@ -257,9 +257,10 @@ namespace AzToolsFramework
|
||||
|
||||
// Select Container Entity
|
||||
{
|
||||
auto selectionUndo = aznew SelectionCommand({ containerEntityId }, "Select Prefab Container Entity");
|
||||
const EntityIdList selectedEntities = EntityIdList{ containerEntityId };
|
||||
auto selectionUndo = aznew SelectionCommand(selectedEntities, "Select Prefab Container Entity");
|
||||
selectionUndo->SetParent(undoBatch.GetUndoBatch());
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::RunRedoSeparately, selectionUndo);
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::SetSelectedEntities, selectedEntities);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1097,7 +1098,7 @@ namespace AzToolsFramework
|
||||
// Select the duplicated entities/instances
|
||||
auto selectionUndo = aznew SelectionCommand(duplicatedEntityAndInstanceIds, "Select Duplicated Entities/Instances");
|
||||
selectionUndo->SetParent(undoBatch.GetUndoBatch());
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::RunRedoSeparately, selectionUndo);
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::SetSelectedEntities, duplicatedEntityAndInstanceIds);
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
|
||||
+2
-1
@@ -313,7 +313,8 @@ namespace AzToolsFramework
|
||||
StyledTreeView::StartCustomDrag(indexListSorted, supportedActions);
|
||||
}
|
||||
|
||||
void EntityOutlinerTreeView::OnEditorFocusChanged([[maybe_unused]] AZ::EntityId entityId)
|
||||
void EntityOutlinerTreeView::OnEditorFocusChanged(
|
||||
[[maybe_unused]] AZ::EntityId previousFocusEntityId, [[maybe_unused]] AZ::EntityId newFocusEntityId)
|
||||
{
|
||||
viewport()->repaint();
|
||||
}
|
||||
|
||||
+1
-1
@@ -64,7 +64,7 @@ namespace AzToolsFramework
|
||||
void leaveEvent(QEvent* event) override;
|
||||
|
||||
// FocusModeNotificationBus overrides ...
|
||||
void OnEditorFocusChanged(AZ::EntityId entityId) override;
|
||||
void OnEditorFocusChanged(AZ::EntityId previousFocusEntityId, AZ::EntityId newFocusEntityId) override;
|
||||
|
||||
//! Renders the left side of the item: appropriate background, branch lines, icons.
|
||||
void drawBranches(QPainter* painter, const QRect& rect, const QModelIndex& index) const override;
|
||||
|
||||
+9
-1
@@ -324,7 +324,8 @@ namespace AzToolsFramework
|
||||
// Currently, the first behavior is implemented.
|
||||
void EntityOutlinerWidget::OnSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
|
||||
{
|
||||
if (m_selectionChangeInProgress || !m_enableSelectionUpdates)
|
||||
if (m_selectionChangeInProgress || !m_enableSelectionUpdates
|
||||
|| (selected.empty() && deselected.empty()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -552,6 +553,13 @@ namespace AzToolsFramework
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not display the context menu if the item under the mouse cursor is not selectable.
|
||||
if (const QModelIndex& index = m_gui->m_objectTree->indexAt(pos); index.isValid()
|
||||
&& (index.flags() & Qt::ItemIsSelectable) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QMenu* contextMenu = new QMenu(this);
|
||||
|
||||
// Populate global context menu.
|
||||
|
||||
+10
-17
@@ -24,11 +24,11 @@
|
||||
#include <AzToolsFramework/AssetBrowser/AssetSelectionModel.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Entries/SourceAssetBrowserEntry.h>
|
||||
#include <AzToolsFramework/ContainerEntity/ContainerEntityInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
|
||||
#include <AzToolsFramework/Prefab/EditorPrefabComponent.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceEntityMapperInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusPublicInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabLoaderInterface.h>
|
||||
#include <AzToolsFramework/Prefab/Procedural/ProceduralPrefabAsset.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceEntityMapperInterface.h>
|
||||
#include <AzToolsFramework/Prefab/EditorPrefabComponent.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorLayerComponentBus.h>
|
||||
#include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiInterface.h>
|
||||
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationInterface.h>
|
||||
@@ -39,7 +39,6 @@
|
||||
#include <AzQtComponents/Components/StyleManager.h>
|
||||
#include <AzQtComponents/Components/Widgets/CardHeader.h>
|
||||
|
||||
|
||||
#include <QApplication>
|
||||
#include <QCheckBox>
|
||||
#include <QDialog>
|
||||
@@ -56,14 +55,13 @@
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
{
|
||||
ContainerEntityInterface* PrefabIntegrationManager::s_containerEntityInterface = nullptr;
|
||||
EditorEntityUiInterface* PrefabIntegrationManager::s_editorEntityUiInterface = nullptr;
|
||||
PrefabFocusInterface* PrefabIntegrationManager::s_prefabFocusInterface = nullptr;
|
||||
PrefabFocusPublicInterface* PrefabIntegrationManager::s_prefabFocusPublicInterface = nullptr;
|
||||
PrefabLoaderInterface* PrefabIntegrationManager::s_prefabLoaderInterface = nullptr;
|
||||
PrefabPublicInterface* PrefabIntegrationManager::s_prefabPublicInterface = nullptr;
|
||||
PrefabSystemComponentInterface* PrefabIntegrationManager::s_prefabSystemComponentInterface = nullptr;
|
||||
@@ -129,10 +127,10 @@ namespace AzToolsFramework
|
||||
return;
|
||||
}
|
||||
|
||||
s_prefabFocusInterface = AZ::Interface<PrefabFocusInterface>::Get();
|
||||
if (s_prefabFocusInterface == nullptr)
|
||||
s_prefabFocusPublicInterface = AZ::Interface<PrefabFocusPublicInterface>::Get();
|
||||
if (s_prefabFocusPublicInterface == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "Prefab - could not get PrefabFocusInterface on PrefabIntegrationManager construction.");
|
||||
AZ_Assert(false, "Prefab - could not get PrefabFocusPublicInterface on PrefabIntegrationManager construction.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -247,12 +245,8 @@ namespace AzToolsFramework
|
||||
if (s_prefabPublicInterface->IsInstanceContainerEntity(selectedEntity))
|
||||
{
|
||||
// Edit Prefab
|
||||
if (prefabWipFeaturesEnabled)
|
||||
if (prefabWipFeaturesEnabled && !s_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(selectedEntity))
|
||||
{
|
||||
bool beingEdited = s_prefabFocusInterface->IsOwningPrefabBeingFocused(selectedEntity);
|
||||
|
||||
if (!beingEdited)
|
||||
{
|
||||
QAction* editAction = menu->addAction(QObject::tr("Edit Prefab"));
|
||||
editAction->setToolTip(QObject::tr("Edit the prefab in focus mode."));
|
||||
|
||||
@@ -261,7 +255,6 @@ namespace AzToolsFramework
|
||||
});
|
||||
|
||||
itemWasShown = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Save Prefab
|
||||
@@ -317,7 +310,7 @@ namespace AzToolsFramework
|
||||
|
||||
void PrefabIntegrationManager::OnEscape()
|
||||
{
|
||||
s_prefabFocusInterface->FocusOnOwningPrefab(AZ::EntityId());
|
||||
s_prefabFocusPublicInterface->FocusOnOwningPrefab(AZ::EntityId());
|
||||
}
|
||||
|
||||
void PrefabIntegrationManager::HandleSourceFileType(AZStd::string_view sourceFilePath, AZ::EntityId parentId, AZ::Vector3 position) const
|
||||
@@ -490,7 +483,7 @@ namespace AzToolsFramework
|
||||
|
||||
void PrefabIntegrationManager::ContextMenu_EditPrefab(AZ::EntityId containerEntity)
|
||||
{
|
||||
s_prefabFocusInterface->FocusOnOwningPrefab(containerEntity);
|
||||
s_prefabFocusPublicInterface->FocusOnOwningPrefab(containerEntity);
|
||||
}
|
||||
|
||||
void PrefabIntegrationManager::ContextMenu_SavePrefab(AZ::EntityId containerEntity)
|
||||
|
||||
+2
-2
@@ -30,7 +30,7 @@ namespace AzToolsFramework
|
||||
|
||||
namespace Prefab
|
||||
{
|
||||
class PrefabFocusInterface;
|
||||
class PrefabFocusPublicInterface;
|
||||
class PrefabLoaderInterface;
|
||||
|
||||
//! Structure for saving/retrieving user settings related to prefab workflows.
|
||||
@@ -144,7 +144,7 @@ namespace AzToolsFramework
|
||||
|
||||
static ContainerEntityInterface* s_containerEntityInterface;
|
||||
static EditorEntityUiInterface* s_editorEntityUiInterface;
|
||||
static PrefabFocusInterface* s_prefabFocusInterface;
|
||||
static PrefabFocusPublicInterface* s_prefabFocusPublicInterface;
|
||||
static PrefabLoaderInterface* s_prefabLoaderInterface;
|
||||
static PrefabPublicInterface* s_prefabPublicInterface;
|
||||
static PrefabSystemComponentInterface* s_prefabSystemComponentInterface;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusPublicInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
|
||||
#include <AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx>
|
||||
|
||||
@@ -35,10 +35,10 @@ namespace AzToolsFramework
|
||||
return;
|
||||
}
|
||||
|
||||
m_prefabFocusInterface = AZ::Interface<Prefab::PrefabFocusInterface>::Get();
|
||||
if (m_prefabFocusInterface == nullptr)
|
||||
m_prefabFocusPublicInterface = AZ::Interface<Prefab::PrefabFocusPublicInterface>::Get();
|
||||
if (m_prefabFocusPublicInterface == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "PrefabUiHandler - could not get PrefabFocusInterface on PrefabUiHandler construction.");
|
||||
AZ_Assert(false, "PrefabUiHandler - could not get PrefabFocusPublicInterface on PrefabUiHandler construction.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -83,7 +83,7 @@ namespace AzToolsFramework
|
||||
|
||||
QIcon PrefabUiHandler::GenerateItemIcon(AZ::EntityId entityId) const
|
||||
{
|
||||
if (m_prefabFocusInterface->IsOwningPrefabBeingFocused(entityId))
|
||||
if (m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId))
|
||||
{
|
||||
return QIcon(m_prefabEditIconPath);
|
||||
}
|
||||
@@ -105,7 +105,7 @@ namespace AzToolsFramework
|
||||
const bool hasVisibleChildren = index.data(EntityOutlinerListModel::ExpandedRole).value<bool>() && index.model()->hasChildren(index);
|
||||
|
||||
QColor backgroundColor = m_prefabCapsuleColor;
|
||||
if (m_prefabFocusInterface->IsOwningPrefabBeingFocused(entityId))
|
||||
if (m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId))
|
||||
{
|
||||
backgroundColor = m_prefabCapsuleEditColor;
|
||||
}
|
||||
@@ -191,7 +191,7 @@ namespace AzToolsFramework
|
||||
const bool isLastColumn = descendantIndex.column() == EntityOutlinerListModel::ColumnLockToggle;
|
||||
|
||||
QColor borderColor = m_prefabCapsuleColor;
|
||||
if (m_prefabFocusInterface->IsOwningPrefabBeingFocused(entityId))
|
||||
if (m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId))
|
||||
{
|
||||
borderColor = m_prefabCapsuleEditColor;
|
||||
}
|
||||
@@ -329,7 +329,7 @@ namespace AzToolsFramework
|
||||
if (prefabWipFeaturesEnabled)
|
||||
{
|
||||
// Focus on this prefab
|
||||
m_prefabFocusInterface->FocusOnOwningPrefab(entityId);
|
||||
m_prefabFocusPublicInterface->FocusOnOwningPrefab(entityId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace AzToolsFramework
|
||||
|
||||
namespace Prefab
|
||||
{
|
||||
class PrefabFocusInterface;
|
||||
class PrefabFocusPublicInterface;
|
||||
class PrefabPublicInterface;
|
||||
};
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace AzToolsFramework
|
||||
void OnDoubleClick(AZ::EntityId entityId) const override;
|
||||
|
||||
private:
|
||||
Prefab::PrefabFocusInterface* m_prefabFocusInterface = nullptr;
|
||||
Prefab::PrefabFocusPublicInterface* m_prefabFocusPublicInterface = nullptr;
|
||||
Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr;
|
||||
|
||||
static bool IsLastVisibleChild(const QModelIndex& parent, const QModelIndex& child);
|
||||
|
||||
+7
-7
@@ -8,7 +8,7 @@
|
||||
|
||||
#include <AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h>
|
||||
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusPublicInterface.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab
|
||||
{
|
||||
@@ -31,8 +31,8 @@ namespace AzToolsFramework::Prefab
|
||||
void PrefabViewportFocusPathHandler::Initialize(AzQtComponents::BreadCrumbs* breadcrumbsWidget, QToolButton* backButton)
|
||||
{
|
||||
// Get reference to the PrefabFocusInterface handler
|
||||
m_prefabFocusInterface = AZ::Interface<PrefabFocusInterface>::Get();
|
||||
if (m_prefabFocusInterface == nullptr)
|
||||
m_prefabFocusPublicInterface = AZ::Interface<PrefabFocusPublicInterface>::Get();
|
||||
if (m_prefabFocusPublicInterface == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "Prefab - could not get PrefabFocusInterface on PrefabViewportFocusPathHandler construction.");
|
||||
return;
|
||||
@@ -46,7 +46,7 @@ namespace AzToolsFramework::Prefab
|
||||
connect(m_breadcrumbsWidget, &AzQtComponents::BreadCrumbs::linkClicked, this,
|
||||
[&](const QString&, int linkIndex)
|
||||
{
|
||||
m_prefabFocusInterface->FocusOnPathIndex(m_editorEntityContextId, linkIndex);
|
||||
m_prefabFocusPublicInterface->FocusOnPathIndex(m_editorEntityContextId, linkIndex);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -54,9 +54,9 @@ namespace AzToolsFramework::Prefab
|
||||
connect(m_backButton, &QToolButton::clicked, this,
|
||||
[&]()
|
||||
{
|
||||
if (int length = m_prefabFocusInterface->GetPrefabFocusPathLength(m_editorEntityContextId); length > 1)
|
||||
if (int length = m_prefabFocusPublicInterface->GetPrefabFocusPathLength(m_editorEntityContextId); length > 1)
|
||||
{
|
||||
m_prefabFocusInterface->FocusOnPathIndex(m_editorEntityContextId, length - 2);
|
||||
m_prefabFocusPublicInterface->FocusOnPathIndex(m_editorEntityContextId, length - 2);
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -65,7 +65,7 @@ namespace AzToolsFramework::Prefab
|
||||
void PrefabViewportFocusPathHandler::OnPrefabFocusChanged()
|
||||
{
|
||||
// Push new Path
|
||||
m_breadcrumbsWidget->pushPath(m_prefabFocusInterface->GetPrefabFocusPath(m_editorEntityContextId).c_str());
|
||||
m_breadcrumbsWidget->pushPath(m_prefabFocusPublicInterface->GetPrefabFocusPath(m_editorEntityContextId).c_str());
|
||||
}
|
||||
|
||||
} // namespace AzToolsFramework::Prefab
|
||||
|
||||
+2
-2
@@ -19,7 +19,7 @@
|
||||
|
||||
namespace AzToolsFramework::Prefab
|
||||
{
|
||||
class PrefabFocusInterface;
|
||||
class PrefabFocusPublicInterface;
|
||||
|
||||
class PrefabViewportFocusPathHandler
|
||||
: public PrefabFocusNotificationBus::Handler
|
||||
@@ -40,6 +40,6 @@ namespace AzToolsFramework::Prefab
|
||||
|
||||
AzFramework::EntityContextId m_editorEntityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
|
||||
PrefabFocusInterface* m_prefabFocusInterface = nullptr;
|
||||
PrefabFocusPublicInterface* m_prefabFocusPublicInterface = nullptr;
|
||||
};
|
||||
} // namespace AzToolsFramework::Prefab
|
||||
|
||||
+2
-3
@@ -38,7 +38,7 @@ namespace AzToolsFramework
|
||||
static bool UnsignedToolTip(QWidget* widget, QString& toolTipString);
|
||||
};
|
||||
|
||||
//! Base class for integer widget handlers to provide functionality independant
|
||||
//! Base class for integer widget handlers to provide functionality independent
|
||||
//! of widget type.
|
||||
//! @tparam ValueType The integer primitive type of the handler.
|
||||
//! @tparam PropertyControl The widget type of the handler.
|
||||
@@ -167,8 +167,7 @@ namespace AzToolsFramework
|
||||
PropertyControl* newCtrl = aznew PropertyControl(pParent);
|
||||
this->connect(newCtrl, &PropertyControl::valueChanged, this, [newCtrl]()
|
||||
{
|
||||
EBUS_EVENT(PropertyEditorGUIMessages::Bus, RequestWrite, newCtrl);
|
||||
AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&PropertyEditorGUIMessages::Bus::Handler::RequestWrite, newCtrl);
|
||||
AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&PropertyEditorGUIMessages::Bus::Events::RequestWrite, newCtrl);
|
||||
});
|
||||
// note: Qt automatically disconnects objects from each other when either end is destroyed, no need to worry about delete.
|
||||
|
||||
|
||||
-5
@@ -98,11 +98,6 @@ namespace AzToolsFramework
|
||||
QWidget* IntSpinBoxHandler<ValueType>::CreateGUI(QWidget* parent)
|
||||
{
|
||||
PropertyIntSpinCtrl* newCtrl = static_cast<PropertyIntSpinCtrl*>(BaseHandler::CreateGUI(parent));
|
||||
this->connect(newCtrl, &PropertyIntSpinCtrl::valueChanged, [newCtrl]()
|
||||
{
|
||||
AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&PropertyEditorGUIMessages::Bus::Handler::RequestWrite, newCtrl);
|
||||
});
|
||||
|
||||
return newCtrl;
|
||||
}
|
||||
|
||||
|
||||
+18
-25
@@ -407,7 +407,7 @@ namespace AzToolsFramework
|
||||
const AzFramework::CameraState cameraState = GetCameraState(viewportId);
|
||||
for (size_t entityCacheIndex = 0; entityCacheIndex < entityDataCache.VisibleEntityDataCount(); ++entityCacheIndex)
|
||||
{
|
||||
if (entityDataCache.IsVisibleEntityLocked(entityCacheIndex) || !entityDataCache.IsVisibleEntityVisible(entityCacheIndex))
|
||||
if (!entityDataCache.IsVisibleEntitySelectableInViewport(entityCacheIndex))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -1113,7 +1113,7 @@ namespace AzToolsFramework
|
||||
});
|
||||
|
||||
m_boxSelect.InstallLeftMouseUp(
|
||||
[this, entityBoxSelectData]()
|
||||
[this, entityBoxSelectData]
|
||||
{
|
||||
entityBoxSelectData->m_boxSelectSelectionCommand->UpdateSelection(EntityIdVectorFromContainer(m_selectedEntityIds));
|
||||
|
||||
@@ -2171,7 +2171,7 @@ namespace AzToolsFramework
|
||||
// lock selection
|
||||
AddAction(
|
||||
m_actions, { QKeySequence(Qt::Key_L) }, LockSelection, LockSelectionTitle, LockSelectionDesc,
|
||||
[lockUnlock]()
|
||||
[lockUnlock]
|
||||
{
|
||||
lockUnlock(true);
|
||||
});
|
||||
@@ -2179,7 +2179,7 @@ namespace AzToolsFramework
|
||||
// unlock selection
|
||||
AddAction(
|
||||
m_actions, { QKeySequence(Qt::CTRL + Qt::Key_L) }, UnlockSelection, LockSelectionTitle, LockSelectionDesc,
|
||||
[lockUnlock]()
|
||||
[lockUnlock]
|
||||
{
|
||||
lockUnlock(false);
|
||||
});
|
||||
@@ -2209,7 +2209,7 @@ namespace AzToolsFramework
|
||||
// hide selection
|
||||
AddAction(
|
||||
m_actions, { QKeySequence(Qt::Key_H) }, HideSelection, HideSelectionTitle, HideSelectionDesc,
|
||||
[showHide]()
|
||||
[showHide]
|
||||
{
|
||||
showHide(false);
|
||||
});
|
||||
@@ -2217,7 +2217,7 @@ namespace AzToolsFramework
|
||||
// show selection
|
||||
AddAction(
|
||||
m_actions, { QKeySequence(Qt::CTRL + Qt::Key_H) }, ShowSelection, HideSelectionTitle, HideSelectionDesc,
|
||||
[showHide]()
|
||||
[showHide]
|
||||
{
|
||||
showHide(true);
|
||||
});
|
||||
@@ -2225,7 +2225,7 @@ namespace AzToolsFramework
|
||||
// unlock all entities in the level/scene
|
||||
AddAction(
|
||||
m_actions, { QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_L) }, UnlockAll, UnlockAllTitle, UnlockAllDesc,
|
||||
[]()
|
||||
[]
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
@@ -2242,14 +2242,14 @@ namespace AzToolsFramework
|
||||
// show all entities in the level/scene
|
||||
AddAction(
|
||||
m_actions, { QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_H) }, ShowAll, ShowAllTitle, ShowAllDesc,
|
||||
[]()
|
||||
[]
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
ScopedUndoBatch undoBatch(ShowAllEntitiesUndoRedoDesc);
|
||||
|
||||
EnumerateEditorEntities(
|
||||
[](AZ::EntityId entityId)
|
||||
[](const AZ::EntityId entityId)
|
||||
{
|
||||
ScopedUndoBatch::MarkEntityDirty(entityId);
|
||||
SetEntityVisibility(entityId, true);
|
||||
@@ -2259,7 +2259,7 @@ namespace AzToolsFramework
|
||||
// select all entities in the level/scene
|
||||
AddAction(
|
||||
m_actions, { QKeySequence(Qt::CTRL + Qt::Key_A) }, SelectAll, SelectAllTitle, SelectAllDesc,
|
||||
[this]()
|
||||
[this]
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
@@ -2299,7 +2299,7 @@ namespace AzToolsFramework
|
||||
// invert current selection
|
||||
AddAction(
|
||||
m_actions, { QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_I) }, InvertSelect, InvertSelectionTitle, InvertSelectionDesc,
|
||||
[this]()
|
||||
[this]
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
@@ -2346,17 +2346,10 @@ namespace AzToolsFramework
|
||||
// duplicate selection
|
||||
AddAction(
|
||||
m_actions, { QKeySequence(Qt::CTRL + Qt::Key_D) }, DuplicateSelect, DuplicateTitle, DuplicateDesc,
|
||||
[]()
|
||||
[]
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
// Clear Widget selection - Prevents issues caused by cloning entities while a property in the Reflected Property Editor
|
||||
// is being edited.
|
||||
if (QApplication::focusWidget())
|
||||
{
|
||||
QApplication::focusWidget()->clearFocus();
|
||||
}
|
||||
|
||||
ScopedUndoBatch undoBatch(DuplicateUndoRedoDesc);
|
||||
auto selectionCommand = AZStd::make_unique<SelectionCommand>(EntityIdList(), DuplicateUndoRedoDesc);
|
||||
selectionCommand->SetParent(undoBatch.GetUndoBatch());
|
||||
@@ -2371,7 +2364,7 @@ namespace AzToolsFramework
|
||||
// delete selection
|
||||
AddAction(
|
||||
m_actions, { QKeySequence(Qt::Key_Delete) }, DeleteSelect, DeleteTitle, DeleteDesc,
|
||||
[this]()
|
||||
[this]
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
@@ -2388,21 +2381,21 @@ namespace AzToolsFramework
|
||||
|
||||
AddAction(
|
||||
m_actions, { QKeySequence(Qt::Key_Space) }, EditEscaspe, "", "",
|
||||
[this]()
|
||||
[this]
|
||||
{
|
||||
DeselectEntities();
|
||||
});
|
||||
|
||||
AddAction(
|
||||
m_actions, { QKeySequence(Qt::Key_P) }, EditPivot, TogglePivotTitleEditMenu, TogglePivotDesc,
|
||||
[this]()
|
||||
[this]
|
||||
{
|
||||
ToggleCenterPivotSelection();
|
||||
});
|
||||
|
||||
AddAction(
|
||||
m_actions, { QKeySequence(Qt::Key_R) }, EditReset, ResetEntityTransformTitle, ResetEntityTransformDesc,
|
||||
[this]()
|
||||
[this]
|
||||
{
|
||||
switch (m_mode)
|
||||
{
|
||||
@@ -2427,7 +2420,7 @@ namespace AzToolsFramework
|
||||
|
||||
AddAction(
|
||||
m_actions, { QKeySequence(Qt::Key_U) }, ViewportUiVisible, "Toggle Viewport UI", "Hide/Show Viewport UI",
|
||||
[this]()
|
||||
[this]
|
||||
{
|
||||
SetAllViewportUiVisible(!m_viewportUiVisible);
|
||||
});
|
||||
@@ -3236,7 +3229,7 @@ namespace AzToolsFramework
|
||||
QAction* action = menu->addAction(QObject::tr(TogglePivotTitleRightClick));
|
||||
QObject::connect(
|
||||
action, &QAction::triggered, action,
|
||||
[this]()
|
||||
[this]
|
||||
{
|
||||
ToggleCenterPivotSelection();
|
||||
});
|
||||
|
||||
+109
-3
@@ -9,7 +9,9 @@
|
||||
#include "EditorVisibleEntityDataCache.h"
|
||||
|
||||
#include <AzCore/std/sort.h>
|
||||
#include <AzToolsFramework/ContainerEntity/ContainerEntityInterface.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityModel.h>
|
||||
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
|
||||
#include <AzToolsFramework/Viewport/ViewportMessages.h>
|
||||
#include <Entity/EditorEntityHelpers.h>
|
||||
|
||||
@@ -21,13 +23,23 @@ namespace AzToolsFramework
|
||||
using ComponentEntityAccentType = Components::EditorSelectionAccentSystemComponent::ComponentEntityAccentType;
|
||||
|
||||
EntityData() = default;
|
||||
EntityData(AZ::EntityId entityId, const AZ::Transform& worldFromLocal, bool locked, bool visible, bool selected, bool iconHidden);
|
||||
EntityData(
|
||||
AZ::EntityId entityId,
|
||||
const AZ::Transform& worldFromLocal,
|
||||
bool locked,
|
||||
bool visible,
|
||||
bool inFocus,
|
||||
bool descendantOfClosedContainer,
|
||||
bool selected,
|
||||
bool iconHidden);
|
||||
|
||||
AZ::Transform m_worldFromLocal;
|
||||
AZ::EntityId m_entityId;
|
||||
ComponentEntityAccentType m_accent = ComponentEntityAccentType::None;
|
||||
bool m_locked = false;
|
||||
bool m_visible = true;
|
||||
bool m_inFocus = true;
|
||||
bool m_descendantOfClosedContainer = false;
|
||||
bool m_selected = false;
|
||||
bool m_iconHidden = false;
|
||||
};
|
||||
@@ -57,12 +69,16 @@ namespace AzToolsFramework
|
||||
const AZ::Transform& worldFromLocal,
|
||||
const bool locked,
|
||||
const bool visible,
|
||||
const bool inFocus,
|
||||
const bool descendantOfClosedContainer,
|
||||
const bool selected,
|
||||
const bool iconHidden)
|
||||
: m_worldFromLocal(worldFromLocal)
|
||||
, m_entityId(entityId)
|
||||
, m_locked(locked)
|
||||
, m_visible(visible)
|
||||
, m_inFocus(inFocus)
|
||||
, m_descendantOfClosedContainer(descendantOfClosedContainer)
|
||||
, m_selected(selected)
|
||||
, m_iconHidden(iconHidden)
|
||||
{
|
||||
@@ -106,6 +122,18 @@ namespace AzToolsFramework
|
||||
bool locked = false;
|
||||
EditorEntityInfoRequestBus::EventResult(locked, entityId, &EditorEntityInfoRequestBus::Events::IsLocked);
|
||||
|
||||
bool inFocus = false;
|
||||
if (auto focusModeInterface = AZ::Interface<FocusModeInterface>::Get())
|
||||
{
|
||||
inFocus = focusModeInterface->IsInFocusSubTree(entityId);
|
||||
}
|
||||
|
||||
bool descendantOfClosedContainer = false;
|
||||
if (ContainerEntityInterface* containerEntityInterface = AZ::Interface<ContainerEntityInterface>::Get())
|
||||
{
|
||||
descendantOfClosedContainer = containerEntityInterface->IsUnderClosedContainerEntity(entityId);
|
||||
}
|
||||
|
||||
bool iconHidden = false;
|
||||
EditorEntityIconComponentRequestBus::EventResult(
|
||||
iconHidden, entityId, &EditorEntityIconComponentRequests::IsEntityIconHiddenInViewport);
|
||||
@@ -113,7 +141,7 @@ namespace AzToolsFramework
|
||||
AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity();
|
||||
AZ::TransformBus::EventResult(worldFromLocal, entityId, &AZ::TransformBus::Events::GetWorldTM);
|
||||
|
||||
return { entityId, worldFromLocal, locked, visible, IsSelected(entityId), iconHidden };
|
||||
return { entityId, worldFromLocal, locked, visible, inFocus, descendantOfClosedContainer, IsSelected(entityId), iconHidden };
|
||||
}
|
||||
|
||||
EditorVisibleEntityDataCache::EditorVisibleEntityDataCache()
|
||||
@@ -126,10 +154,17 @@ namespace AzToolsFramework
|
||||
EntitySelectionEvents::Bus::Router::BusRouterConnect();
|
||||
EditorEntityIconComponentNotificationBus::Router::BusRouterConnect();
|
||||
ToolsApplicationNotificationBus::Handler::BusConnect();
|
||||
|
||||
AzFramework::EntityContextId editorEntityContextId = AzToolsFramework::GetEntityContextId();
|
||||
|
||||
ContainerEntityNotificationBus::Handler::BusConnect(editorEntityContextId);
|
||||
FocusModeNotificationBus::Handler::BusConnect(editorEntityContextId);
|
||||
}
|
||||
|
||||
EditorVisibleEntityDataCache::~EditorVisibleEntityDataCache()
|
||||
{
|
||||
FocusModeNotificationBus::Handler::BusDisconnect();
|
||||
ContainerEntityNotificationBus::Handler::BusDisconnect();
|
||||
ToolsApplicationNotificationBus::Handler::BusDisconnect();
|
||||
EditorEntityIconComponentNotificationBus::Router::BusRouterDisconnect();
|
||||
EntitySelectionEvents::Bus::Router::BusRouterDisconnect();
|
||||
@@ -260,7 +295,10 @@ namespace AzToolsFramework
|
||||
|
||||
bool EditorVisibleEntityDataCache::IsVisibleEntitySelectableInViewport(size_t index) const
|
||||
{
|
||||
return m_impl->m_visibleEntityDatas[index].m_visible && !m_impl->m_visibleEntityDatas[index].m_locked;
|
||||
return m_impl->m_visibleEntityDatas[index].m_visible
|
||||
&& !m_impl->m_visibleEntityDatas[index].m_locked
|
||||
&& m_impl->m_visibleEntityDatas[index].m_inFocus
|
||||
&& !m_impl->m_visibleEntityDatas[index].m_descendantOfClosedContainer;
|
||||
}
|
||||
|
||||
AZStd::optional<size_t> EditorVisibleEntityDataCache::GetVisibleEntityIndexFromId(const AZ::EntityId entityId) const
|
||||
@@ -371,4 +409,72 @@ namespace AzToolsFramework
|
||||
m_impl->m_visibleEntityDatas[entityIndex.value()].m_iconHidden = iconHidden;
|
||||
}
|
||||
}
|
||||
|
||||
void EditorVisibleEntityDataCache::OnContainerEntityStatusChanged(AZ::EntityId entityId, [[maybe_unused]] bool open)
|
||||
{
|
||||
// Get container descendants
|
||||
AzToolsFramework::EntityIdList descendantIds;
|
||||
AZ::TransformBus::EventResult(descendantIds, entityId, &AZ::TransformBus::Events::GetAllDescendants);
|
||||
|
||||
// Update cached values
|
||||
if (auto containerEntityInterface = AZ::Interface<ContainerEntityInterface>::Get())
|
||||
{
|
||||
for (AZ::EntityId descendantId : descendantIds)
|
||||
{
|
||||
if (AZStd::optional<size_t> entityIndex = GetVisibleEntityIndexFromId(descendantId))
|
||||
{
|
||||
m_impl->m_visibleEntityDatas[entityIndex.value()].m_descendantOfClosedContainer =
|
||||
containerEntityInterface->IsUnderClosedContainerEntity(descendantId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EditorVisibleEntityDataCache::OnEditorFocusChanged(AZ::EntityId previousFocusEntityId, AZ::EntityId newFocusEntityId)
|
||||
{
|
||||
if (previousFocusEntityId.IsValid() && newFocusEntityId.IsValid())
|
||||
{
|
||||
// Get previous focus root descendants
|
||||
AzToolsFramework::EntityIdList previousDescendantIds;
|
||||
AZ::TransformBus::EventResult(previousDescendantIds, previousFocusEntityId, &AZ::TransformBus::Events::GetAllDescendants);
|
||||
|
||||
// Get new focus root descendants
|
||||
AzToolsFramework::EntityIdList newDescendantIds;
|
||||
AZ::TransformBus::EventResult(newDescendantIds, newFocusEntityId, &AZ::TransformBus::Events::GetAllDescendants);
|
||||
|
||||
// Merge EntityId Lists to avoid refreshing values twice
|
||||
AzToolsFramework::EntityIdSet descendantsSet;
|
||||
descendantsSet.insert(previousFocusEntityId);
|
||||
descendantsSet.insert(newFocusEntityId);
|
||||
descendantsSet.insert(previousDescendantIds.begin(), previousDescendantIds.end());
|
||||
descendantsSet.insert(newDescendantIds.begin(), newDescendantIds.end());
|
||||
|
||||
// Update cached values
|
||||
if (auto focusModeInterface = AZ::Interface<FocusModeInterface>::Get())
|
||||
{
|
||||
for (const AZ::EntityId& descendantId : descendantsSet)
|
||||
{
|
||||
if (AZStd::optional<size_t> entityIndex = GetVisibleEntityIndexFromId(descendantId))
|
||||
{
|
||||
m_impl->m_visibleEntityDatas[entityIndex.value()].m_inFocus = focusModeInterface->IsInFocusSubTree(descendantId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If either focus was the invalid entity, refresh all entities.
|
||||
if (auto focusModeInterface = AZ::Interface<FocusModeInterface>::Get())
|
||||
{
|
||||
for (size_t entityIndex = 0; entityIndex < m_impl->m_visibleEntityDatas.size(); ++entityIndex)
|
||||
{
|
||||
if (AZ::EntityId descendantId = GetVisibleEntityId(entityIndex); descendantId.IsValid())
|
||||
{
|
||||
m_impl->m_visibleEntityDatas[entityIndex].m_inFocus = focusModeInterface->IsInFocusSubTree(descendantId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
+17
-7
@@ -11,6 +11,8 @@
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/std/optional.h>
|
||||
#include <AzToolsFramework/API/ComponentEntitySelectionBus.h>
|
||||
#include <AzToolsFramework/ContainerEntity/ContainerEntityNotificationBus.h>
|
||||
#include <AzToolsFramework/FocusMode/FocusModeNotificationBus.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorEntityIconComponentBus.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorLockComponentBus.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorSelectionAccentSystemComponent.h>
|
||||
@@ -28,6 +30,8 @@ namespace AzToolsFramework
|
||||
, private EntitySelectionEvents::Bus::Router
|
||||
, private EditorEntityIconComponentNotificationBus::Router
|
||||
, private ToolsApplicationNotificationBus::Handler
|
||||
, private ContainerEntityNotificationBus::Handler
|
||||
, private FocusModeNotificationBus::Handler
|
||||
{
|
||||
public:
|
||||
EditorVisibleEntityDataCache();
|
||||
@@ -58,28 +62,34 @@ namespace AzToolsFramework
|
||||
void AddEntityIds(const EntityIdList& entityIds);
|
||||
|
||||
private:
|
||||
// ToolsApplicationNotificationBus
|
||||
// ToolsApplicationNotificationBus overrides ...
|
||||
void AfterUndoRedo() override;
|
||||
|
||||
// EditorEntityVisibilityNotificationBus
|
||||
// EditorEntityVisibilityNotificationBus overrides ...
|
||||
void OnEntityVisibilityChanged(bool visibility) override;
|
||||
|
||||
// EditorEntityLockComponentNotificationBus
|
||||
// EditorEntityLockComponentNotificationBus overrides ...
|
||||
void OnEntityLockChanged(bool locked) override;
|
||||
|
||||
// TransformNotificationBus
|
||||
// TransformNotificationBus overrides ...
|
||||
void OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) override;
|
||||
|
||||
// EditorComponentSelectionNotificationsBus
|
||||
// EditorComponentSelectionNotificationsBus overrides ...
|
||||
void OnAccentTypeChanged(EntityAccentType accent) override;
|
||||
|
||||
// EntitySelectionEvents::Bus
|
||||
// EntitySelectionEvents::Bus overrides ...
|
||||
void OnSelected() override;
|
||||
void OnDeselected() override;
|
||||
|
||||
// EditorEntityIconComponentNotificationBus
|
||||
// EditorEntityIconComponentNotificationBus overrides ...
|
||||
void OnEntityIconChanged(const AZ::Data::AssetId& entityIconAssetId) override;
|
||||
|
||||
// ContainerEntityNotificationBus overrides ...
|
||||
void OnContainerEntityStatusChanged(AZ::EntityId entityId, bool open) override;
|
||||
|
||||
// FocusModeNotificationBus overrides ...
|
||||
void OnEditorFocusChanged(AZ::EntityId previousFocusEntityId, AZ::EntityId newFocusEntityId) override;
|
||||
|
||||
class EditorVisibleEntityDataCacheImpl;
|
||||
AZStd::unique_ptr<EditorVisibleEntityDataCacheImpl> m_impl; //!< Internal representation of entity data cache.
|
||||
};
|
||||
|
||||
@@ -646,6 +646,9 @@ set(FILES
|
||||
Prefab/PrefabFocusHandler.cpp
|
||||
Prefab/PrefabFocusInterface.h
|
||||
Prefab/PrefabFocusNotificationBus.h
|
||||
Prefab/PrefabFocusPublicInterface.h
|
||||
Prefab/PrefabFocusUndo.h
|
||||
Prefab/PrefabFocusUndo.cpp
|
||||
Prefab/PrefabIdTypes.h
|
||||
Prefab/PrefabLoader.h
|
||||
Prefab/PrefabLoader.cpp
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
|
||||
#include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusPublicInterface.h>
|
||||
#include <Prefab/PrefabTestFixture.h>
|
||||
|
||||
namespace UnitTest
|
||||
@@ -72,6 +73,9 @@ namespace UnitTest
|
||||
m_prefabFocusInterface = AZ::Interface<PrefabFocusInterface>::Get();
|
||||
ASSERT_TRUE(m_prefabFocusInterface != nullptr);
|
||||
|
||||
m_prefabFocusPublicInterface = AZ::Interface<PrefabFocusPublicInterface>::Get();
|
||||
ASSERT_TRUE(m_prefabFocusPublicInterface != nullptr);
|
||||
|
||||
AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(
|
||||
m_editorEntityContextId, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetEditorEntityContextId);
|
||||
|
||||
@@ -91,6 +95,7 @@ namespace UnitTest
|
||||
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> m_rootInstance;
|
||||
|
||||
PrefabFocusInterface* m_prefabFocusInterface = nullptr;
|
||||
PrefabFocusPublicInterface* m_prefabFocusPublicInterface = nullptr;
|
||||
AzFramework::EntityContextId m_editorEntityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
|
||||
inline static const char* CityEntityName = "City";
|
||||
@@ -105,7 +110,7 @@ namespace UnitTest
|
||||
{
|
||||
// Verify FocusOnOwningPrefab works when passing the container entity of the root prefab.
|
||||
{
|
||||
m_prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap[CityEntityName]->GetContainerEntityId());
|
||||
m_prefabFocusPublicInterface->FocusOnOwningPrefab(m_instanceMap[CityEntityName]->GetContainerEntityId());
|
||||
EXPECT_EQ(
|
||||
m_prefabFocusInterface->GetFocusedPrefabTemplateId(m_editorEntityContextId),
|
||||
m_instanceMap[CityEntityName]->GetTemplateId());
|
||||
@@ -120,7 +125,7 @@ namespace UnitTest
|
||||
{
|
||||
// Verify FocusOnOwningPrefab works when passing a nested entity of the root prefab.
|
||||
{
|
||||
m_prefabFocusInterface->FocusOnOwningPrefab(m_entityMap[CityEntityName]->GetId());
|
||||
m_prefabFocusPublicInterface->FocusOnOwningPrefab(m_entityMap[CityEntityName]->GetId());
|
||||
EXPECT_EQ(
|
||||
m_prefabFocusInterface->GetFocusedPrefabTemplateId(m_editorEntityContextId),
|
||||
m_instanceMap[CityEntityName]->GetTemplateId());
|
||||
@@ -135,7 +140,7 @@ namespace UnitTest
|
||||
{
|
||||
// Verify FocusOnOwningPrefab works when passing the container entity of a nested prefab.
|
||||
{
|
||||
m_prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap[CarEntityName]->GetContainerEntityId());
|
||||
m_prefabFocusPublicInterface->FocusOnOwningPrefab(m_instanceMap[CarEntityName]->GetContainerEntityId());
|
||||
EXPECT_EQ(
|
||||
m_prefabFocusInterface->GetFocusedPrefabTemplateId(m_editorEntityContextId), m_instanceMap[CarEntityName]->GetTemplateId());
|
||||
|
||||
@@ -149,7 +154,7 @@ namespace UnitTest
|
||||
{
|
||||
// Verify FocusOnOwningPrefab works when passing a nested entity of the a nested prefab.
|
||||
{
|
||||
m_prefabFocusInterface->FocusOnOwningPrefab(m_entityMap[Passenger1EntityName]->GetId());
|
||||
m_prefabFocusPublicInterface->FocusOnOwningPrefab(m_entityMap[Passenger1EntityName]->GetId());
|
||||
EXPECT_EQ(
|
||||
m_prefabFocusInterface->GetFocusedPrefabTemplateId(m_editorEntityContextId), m_instanceMap[CarEntityName]->GetTemplateId());
|
||||
|
||||
@@ -169,7 +174,7 @@ namespace UnitTest
|
||||
prefabEditorEntityOwnershipInterface->GetRootPrefabInstance();
|
||||
EXPECT_TRUE(rootPrefabInstance.has_value());
|
||||
|
||||
m_prefabFocusInterface->FocusOnOwningPrefab(AZ::EntityId());
|
||||
m_prefabFocusPublicInterface->FocusOnOwningPrefab(AZ::EntityId());
|
||||
EXPECT_EQ(
|
||||
m_prefabFocusInterface->GetFocusedPrefabTemplateId(m_editorEntityContextId), rootPrefabInstance->get().GetTemplateId());
|
||||
|
||||
@@ -183,10 +188,10 @@ namespace UnitTest
|
||||
{
|
||||
// Verify IsOwningPrefabBeingFocused returns true for all entities in a focused prefab (container/nested)
|
||||
{
|
||||
m_prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap[CityEntityName]->GetContainerEntityId());
|
||||
m_prefabFocusPublicInterface->FocusOnOwningPrefab(m_instanceMap[CityEntityName]->GetContainerEntityId());
|
||||
|
||||
EXPECT_TRUE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[CityEntityName]->GetContainerEntityId()));
|
||||
EXPECT_TRUE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap[CityEntityName]->GetId()));
|
||||
EXPECT_TRUE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_instanceMap[CityEntityName]->GetContainerEntityId()));
|
||||
EXPECT_TRUE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_entityMap[CityEntityName]->GetId()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,13 +199,13 @@ namespace UnitTest
|
||||
{
|
||||
// Verify IsOwningPrefabBeingFocused returns false for all entities not in a focused prefab (ancestors/descendants)
|
||||
{
|
||||
m_prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap[StreetEntityName]->GetContainerEntityId());
|
||||
m_prefabFocusPublicInterface->FocusOnOwningPrefab(m_instanceMap[StreetEntityName]->GetContainerEntityId());
|
||||
|
||||
EXPECT_TRUE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[StreetEntityName]->GetContainerEntityId()));
|
||||
EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[CityEntityName]->GetContainerEntityId()));
|
||||
EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap[CityEntityName]->GetId()));
|
||||
EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[CarEntityName]->GetContainerEntityId()));
|
||||
EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap[Passenger1EntityName]->GetId()));
|
||||
EXPECT_TRUE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_instanceMap[StreetEntityName]->GetContainerEntityId()));
|
||||
EXPECT_FALSE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_instanceMap[CityEntityName]->GetContainerEntityId()));
|
||||
EXPECT_FALSE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_entityMap[CityEntityName]->GetId()));
|
||||
EXPECT_FALSE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_instanceMap[CarEntityName]->GetContainerEntityId()));
|
||||
EXPECT_FALSE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_entityMap[Passenger1EntityName]->GetId()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,12 +213,12 @@ namespace UnitTest
|
||||
{
|
||||
// Verify IsOwningPrefabBeingFocused returns false for all entities not in a focused prefab (siblings)
|
||||
{
|
||||
m_prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap[SportsCarEntityName]->GetContainerEntityId());
|
||||
m_prefabFocusPublicInterface->FocusOnOwningPrefab(m_instanceMap[SportsCarEntityName]->GetContainerEntityId());
|
||||
|
||||
EXPECT_TRUE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[SportsCarEntityName]->GetContainerEntityId()));
|
||||
EXPECT_TRUE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap[Passenger2EntityName]->GetId()));
|
||||
EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[CarEntityName]->GetContainerEntityId()));
|
||||
EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap[Passenger1EntityName]->GetId()));
|
||||
EXPECT_TRUE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_instanceMap[SportsCarEntityName]->GetContainerEntityId()));
|
||||
EXPECT_TRUE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_entityMap[Passenger2EntityName]->GetId()));
|
||||
EXPECT_FALSE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_instanceMap[CarEntityName]->GetContainerEntityId()));
|
||||
EXPECT_FALSE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_entityMap[Passenger1EntityName]->GetId()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <AzCore/Math/Crc.h>
|
||||
#include <AzCore/Math/Quaternion.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/std/allocator_stateless.h>
|
||||
|
||||
#include <Range.h>
|
||||
#include <AnimKey.h>
|
||||
@@ -181,7 +182,7 @@ public:
|
||||
|
||||
private:
|
||||
AnimParamType m_type;
|
||||
AZStd::string m_name;
|
||||
AZStd::basic_string<char, AZStd::char_traits<char>, AZStd::stateless_allocator> m_name;
|
||||
};
|
||||
|
||||
namespace AZStd
|
||||
@@ -617,7 +618,7 @@ public:
|
||||
, valueType(_valueType)
|
||||
, flags(_flags) {};
|
||||
|
||||
AZStd::string name; // parameter name.
|
||||
AZStd::basic_string<char, AZStd::char_traits<char>, AZStd::stateless_allocator> name; // parameter name.
|
||||
CAnimParamType paramType; // parameter id.
|
||||
AnimValueType valueType; // value type, defines type of track to use for animating this parameter.
|
||||
ESupportedParamFlags flags; // combination of flags from ESupportedParamFlags.
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <AzCore/std/allocator_stateless.h>
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
#include <AzCore/std/containers/unordered_set.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
@@ -491,6 +492,13 @@ namespace stl
|
||||
return type;
|
||||
}
|
||||
|
||||
//! Specialization of string to const char cast.
|
||||
template <>
|
||||
inline const char* constchar_cast(const AZStd::basic_string<char, AZStd::char_traits<char>, AZStd::stateless_allocator>& type)
|
||||
{
|
||||
return type.c_str();
|
||||
}
|
||||
|
||||
//! Specialization of string to const char cast.
|
||||
template <>
|
||||
inline const char* constchar_cast(const AZStd::string& type)
|
||||
|
||||
@@ -27,7 +27,7 @@ ly_add_target(
|
||||
3rdParty::expat
|
||||
3rdParty::lz4
|
||||
3rdParty::md5
|
||||
3rdParty::tiff
|
||||
3rdParty::TIFF
|
||||
3rdParty::zstd
|
||||
Legacy::CryCommon
|
||||
Legacy::CrySystem.XMLBinary
|
||||
|
||||
+8
-6
@@ -590,20 +590,22 @@ TEST_F(PlatformConfigurationUnitTests, Test_GemHandling)
|
||||
|
||||
AssetUtilities::ResetAssetRoot();
|
||||
|
||||
ASSERT_EQ(2, config.GetScanFolderCount());
|
||||
ASSERT_EQ(4, config.GetScanFolderCount());
|
||||
EXPECT_FALSE(config.GetScanFolderAt(0).IsRoot());
|
||||
EXPECT_TRUE(config.GetScanFolderAt(0).RecurseSubFolders());
|
||||
// the first one is a game gem, so its order should be above 1 but below 100.
|
||||
EXPECT_GE(config.GetScanFolderAt(0).GetOrder(), 100);
|
||||
EXPECT_EQ(0, config.GetScanFolderAt(0).ScanPath().compare(expectedScanFolder, Qt::CaseInsensitive));
|
||||
|
||||
// for each gem, there are currently 1 scan folder, the gem assets folder, with no output prefix
|
||||
// for each gem, there are currently 2 scan folders:
|
||||
// The Gem's 'Assets' folder
|
||||
// The Gem's 'Registry' folder
|
||||
|
||||
expectedScanFolder = tempPath.absoluteFilePath("Gems/LmbrCentral/v2/Assets");
|
||||
EXPECT_FALSE(config.GetScanFolderAt(1).IsRoot() );
|
||||
EXPECT_TRUE(config.GetScanFolderAt(1).RecurseSubFolders());
|
||||
EXPECT_GT(config.GetScanFolderAt(1).GetOrder(), config.GetScanFolderAt(0).GetOrder());
|
||||
EXPECT_EQ(0, config.GetScanFolderAt(1).ScanPath().compare(expectedScanFolder, Qt::CaseInsensitive));
|
||||
EXPECT_FALSE(config.GetScanFolderAt(2).IsRoot() );
|
||||
EXPECT_TRUE(config.GetScanFolderAt(2).RecurseSubFolders());
|
||||
EXPECT_GT(config.GetScanFolderAt(2).GetOrder(), config.GetScanFolderAt(0).GetOrder());
|
||||
EXPECT_EQ(0, config.GetScanFolderAt(2).ScanPath().compare(expectedScanFolder, Qt::CaseInsensitive));
|
||||
}
|
||||
|
||||
TEST_F(PlatformConfigurationUnitTests, Test_MetaFileTypes)
|
||||
|
||||
@@ -1582,6 +1582,24 @@ namespace AssetProcessor
|
||||
gemOrder,
|
||||
/*scanFolderId*/ 0,
|
||||
/*canSaveNewAssets*/ true)); // Users can create assets like slices in Gem asset folders.
|
||||
|
||||
// Now add another scan folder on Gem/GemName/Registry...
|
||||
gemFolder = gemDir.absoluteFilePath(AzFramework::GemInfo::GetGemRegistryFolder());
|
||||
gemFolder = AssetUtilities::NormalizeDirectoryPath(gemFolder);
|
||||
|
||||
assetBrowserDisplayName = AzFramework::GemInfo::GetGemRegistryFolder();
|
||||
portableKey = QString("gemregistry-%1").arg(gemNameAsUuid);
|
||||
gemOrder++;
|
||||
|
||||
AZ_TracePrintf(AssetProcessor::DebugChannel, "Adding GEM registry folder for monitoring / scanning: %s.\n", gemFolder.toUtf8().data());
|
||||
AddScanFolder(ScanFolderInfo(
|
||||
gemFolder,
|
||||
assetBrowserDisplayName,
|
||||
portableKey,
|
||||
isRoot,
|
||||
isRecursive,
|
||||
platforms,
|
||||
gemOrder));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="11" viewBox="0 0 16 11" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.7872 2.91865C11.4965 1.25029 10.0476 0 8.31169 0C6.91408 0 5.66005 0.823481 5.09382 2.08042C5.05787 2.07875 5.02234 2.07792 4.98701 2.07792C3.81195 2.07792 2.7921 2.8241 2.42743 3.92062C0.986389 4.39377 0 5.73881 0 7.27273C0 9.22057 1.58462 10.8052 3.53247 10.8052H12.0519C14.229 10.8052 16 9.03418 16 6.85714C16 4.59242 14.085 2.75595 11.7872 2.91865ZM8.31169 9.35065L5.61039 6.44156H7.27273V4.57143H9.35065V6.44156H11.013L8.31169 9.35065Z" fill="#E4E8EB"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 575 B |
@@ -34,9 +34,11 @@
|
||||
<file>Warning.svg</file>
|
||||
<file>Backgrounds/DefaultBackground.jpg</file>
|
||||
<file>Backgrounds/FtueBackground.jpg</file>
|
||||
<file>FeatureTagClose.svg</file>
|
||||
<file>X.svg</file>
|
||||
<file>Refresh.svg</file>
|
||||
<file>Edit.svg</file>
|
||||
<file>Delete.svg</file>
|
||||
<file>Download.svg</file>
|
||||
<file>in_progress.gif</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
|
Before Width: | Height: | Size: 400 B After Width: | Height: | Size: 400 B |
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:64985a78205da45f4bb92b040c348d96fe7cd7277549c1f79c430469a0d3bab7
|
||||
size 166393
|
||||
@@ -33,7 +33,7 @@ namespace O3DE::ProjectManager
|
||||
|
||||
m_closeButton = new QPushButton();
|
||||
m_closeButton->setFlat(true);
|
||||
m_closeButton->setIcon(QIcon(":/FeatureTagClose.svg"));
|
||||
m_closeButton->setIcon(QIcon(":/X.svg"));
|
||||
m_closeButton->setIconSize(QSize(12, 12));
|
||||
m_closeButton->setStyleSheet("QPushButton { background-color: transparent; border: 0px }");
|
||||
layout->addWidget(m_closeButton);
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#include "GemInfo.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
GemInfo::GemInfo(const QString& name, const QString& creator, const QString& summary, Platforms platforms, bool isAdded)
|
||||
@@ -29,17 +31,17 @@ namespace O3DE::ProjectManager
|
||||
switch (platform)
|
||||
{
|
||||
case Android:
|
||||
return "Android";
|
||||
return QObject::tr("Android");
|
||||
case iOS:
|
||||
return "iOS";
|
||||
return QObject::tr("iOS");
|
||||
case Linux:
|
||||
return "Linux";
|
||||
return QObject::tr("Linux");
|
||||
case macOS:
|
||||
return "macOS";
|
||||
return QObject::tr("macOS");
|
||||
case Windows:
|
||||
return "Windows";
|
||||
return QObject::tr("Windows");
|
||||
default:
|
||||
return "<Unknown Platform>";
|
||||
return QObject::tr("<Unknown Platform>");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,13 +50,13 @@ namespace O3DE::ProjectManager
|
||||
switch (type)
|
||||
{
|
||||
case Asset:
|
||||
return "Asset";
|
||||
return QObject::tr("Asset");
|
||||
case Code:
|
||||
return "Code";
|
||||
return QObject::tr("Code");
|
||||
case Tool:
|
||||
return "Tool";
|
||||
return QObject::tr("Tool");
|
||||
default:
|
||||
return "<Unknown Type>";
|
||||
return QObject::tr("<Unknown Type>");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,15 +64,33 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
switch (origin)
|
||||
{
|
||||
case Open3DEEngine:
|
||||
return "Open 3D Engine";
|
||||
case Open3DEngine:
|
||||
return QObject::tr("Open 3D Engine");
|
||||
case Local:
|
||||
return "Local";
|
||||
return QObject::tr("Local");
|
||||
case Remote:
|
||||
return QObject::tr("Remote");
|
||||
default:
|
||||
return "<Unknown Gem Origin>";
|
||||
return QObject::tr("<Unknown Gem Origin>");
|
||||
}
|
||||
}
|
||||
|
||||
QString GemInfo::GetDownloadStatusString(DownloadStatus status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case NotDownloaded:
|
||||
return QObject::tr("Not Downloaded");
|
||||
case Downloading:
|
||||
return QObject::tr("Downloading");
|
||||
case Downloaded:
|
||||
return QObject::tr("Downloaded");
|
||||
case UnknownDownloadStatus:
|
||||
default:
|
||||
return QObject::tr("<Unknown Download Status>");
|
||||
}
|
||||
};
|
||||
|
||||
bool GemInfo::IsPlatformSupported(Platform platform) const
|
||||
{
|
||||
return (m_platforms & platform);
|
||||
|
||||
@@ -44,13 +44,23 @@ namespace O3DE::ProjectManager
|
||||
|
||||
enum GemOrigin
|
||||
{
|
||||
Open3DEEngine = 1 << 0,
|
||||
Open3DEngine = 1 << 0,
|
||||
Local = 1 << 1,
|
||||
NumGemOrigins = 2
|
||||
Remote = 1 << 2,
|
||||
NumGemOrigins = 3
|
||||
};
|
||||
Q_DECLARE_FLAGS(GemOrigins, GemOrigin)
|
||||
static QString GetGemOriginString(GemOrigin origin);
|
||||
|
||||
enum DownloadStatus
|
||||
{
|
||||
UnknownDownloadStatus = -1,
|
||||
NotDownloaded,
|
||||
Downloading,
|
||||
Downloaded,
|
||||
};
|
||||
static QString GetDownloadStatusString(DownloadStatus status);
|
||||
|
||||
GemInfo() = default;
|
||||
GemInfo(const QString& name, const QString& creator, const QString& summary, Platforms platforms, bool isAdded);
|
||||
bool IsPlatformSupported(Platform platform) const;
|
||||
@@ -68,6 +78,7 @@ namespace O3DE::ProjectManager
|
||||
QString m_summary = "No summary provided.";
|
||||
Platforms m_platforms;
|
||||
Types m_types; //! Asset and/or Code and/or Tool
|
||||
DownloadStatus m_downloadStatus = UnknownDownloadStatus;
|
||||
QStringList m_features;
|
||||
QString m_requirement;
|
||||
QString m_directoryLink;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <GemCatalog/GemModel.h>
|
||||
#include <GemCatalog/GemSortFilterProxyModel.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
|
||||
#include <QEvent>
|
||||
#include <QAbstractItemView>
|
||||
#include <QPainter>
|
||||
@@ -20,6 +21,7 @@
|
||||
#include <QTextDocument>
|
||||
#include <QAbstractTextDocumentLayout>
|
||||
#include <QDesktopServices>
|
||||
#include <QMovie>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
@@ -32,6 +34,11 @@ namespace O3DE::ProjectManager
|
||||
AddPlatformIcon(GemInfo::Linux, ":/Linux.svg");
|
||||
AddPlatformIcon(GemInfo::macOS, ":/macOS.svg");
|
||||
AddPlatformIcon(GemInfo::Windows, ":/Windows.svg");
|
||||
|
||||
SetStatusIcon(m_notDownloadedPixmap, ":/Download.svg");
|
||||
SetStatusIcon(m_unknownStatusPixmap, ":/X.svg");
|
||||
|
||||
m_downloadingMovie = new QMovie(":/in_progress.gif");
|
||||
}
|
||||
|
||||
void GemItemDelegate::AddPlatformIcon(GemInfo::Platform platform, const QString& iconPath)
|
||||
@@ -41,6 +48,25 @@ namespace O3DE::ProjectManager
|
||||
m_platformIcons.insert(platform, QIcon(iconPath).pixmap(static_cast<int>(static_cast<qreal>(s_platformIconSize) * aspectRatio), s_platformIconSize));
|
||||
}
|
||||
|
||||
void GemItemDelegate::SetStatusIcon(QPixmap& m_iconPixmap, const QString& iconPath)
|
||||
{
|
||||
QPixmap pixmap(iconPath);
|
||||
float aspectRatio = static_cast<float>(pixmap.width()) / pixmap.height();
|
||||
int xScaler = s_statusIconSize;
|
||||
int yScaler = s_statusIconSize;
|
||||
|
||||
if (aspectRatio > 1.0f)
|
||||
{
|
||||
yScaler = static_cast<int>(1.0f / aspectRatio * s_statusIconSize);
|
||||
}
|
||||
else if (aspectRatio < 1.0f)
|
||||
{
|
||||
xScaler = static_cast<int>(aspectRatio * s_statusIconSize);
|
||||
}
|
||||
|
||||
m_iconPixmap = QPixmap(QIcon(iconPath).pixmap(xScaler, yScaler));
|
||||
}
|
||||
|
||||
void GemItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const
|
||||
{
|
||||
if (!modelIndex.isValid())
|
||||
@@ -56,6 +82,8 @@ namespace O3DE::ProjectManager
|
||||
QRect fullRect, itemRect, contentRect;
|
||||
CalcRects(options, fullRect, itemRect, contentRect);
|
||||
|
||||
QRect buttonRect = CalcButtonRect(contentRect);
|
||||
|
||||
QFont standardFont(options.font);
|
||||
standardFont.setPixelSize(static_cast<int>(s_fontSize));
|
||||
QFontMetrics standardFontMetrics(standardFont);
|
||||
@@ -114,7 +142,8 @@ namespace O3DE::ProjectManager
|
||||
const QRect summaryRect = CalcSummaryRect(contentRect, hasTags);
|
||||
DrawText(summary, painter, summaryRect, standardFont);
|
||||
|
||||
DrawButton(painter, contentRect, modelIndex);
|
||||
DrawDownloadStatusIcon(painter, contentRect, buttonRect, modelIndex);
|
||||
DrawButton(painter, buttonRect, modelIndex);
|
||||
DrawPlatformIcons(painter, contentRect, modelIndex);
|
||||
DrawFeatureTags(painter, contentRect, featureTags, standardFont, summaryRect);
|
||||
|
||||
@@ -270,7 +299,7 @@ namespace O3DE::ProjectManager
|
||||
|
||||
QRect GemItemDelegate::CalcButtonRect(const QRect& contentRect) const
|
||||
{
|
||||
const QPoint topLeft = QPoint(contentRect.right() - s_buttonWidth - s_itemMargins.right(), contentRect.top() + contentRect.height() / 2 - s_buttonHeight / 2);
|
||||
const QPoint topLeft = QPoint(contentRect.right() - s_buttonWidth, contentRect.center().y() - s_buttonHeight / 2);
|
||||
const QSize size = QSize(s_buttonWidth, s_buttonHeight);
|
||||
return QRect(topLeft, size);
|
||||
}
|
||||
@@ -378,10 +407,9 @@ namespace O3DE::ProjectManager
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
void GemItemDelegate::DrawButton(QPainter* painter, const QRect& contentRect, const QModelIndex& modelIndex) const
|
||||
void GemItemDelegate::DrawButton(QPainter* painter, const QRect& buttonRect, const QModelIndex& modelIndex) const
|
||||
{
|
||||
painter->save();
|
||||
const QRect buttonRect = CalcButtonRect(contentRect);
|
||||
QPoint circleCenter;
|
||||
|
||||
if (GemModel::IsAdded(modelIndex))
|
||||
@@ -427,4 +455,45 @@ namespace O3DE::ProjectManager
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
void GemItemDelegate::DrawDownloadStatusIcon(QPainter* painter, const QRect& contentRect, const QRect& buttonRect, const QModelIndex& modelIndex) const
|
||||
{
|
||||
const GemInfo::DownloadStatus downloadStatus = GemModel::GetDownloadStatus(modelIndex);
|
||||
|
||||
// Show no icon if gem is already downloaded
|
||||
if (downloadStatus == GemInfo::DownloadStatus::Downloaded)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QPixmap currentFrame;
|
||||
const QPixmap* statusPixmap;
|
||||
if (downloadStatus == GemInfo::DownloadStatus::Downloading)
|
||||
{
|
||||
if (m_downloadingMovie->state() != QMovie::Running)
|
||||
{
|
||||
m_downloadingMovie->start();
|
||||
emit MovieStartedPlaying(m_downloadingMovie);
|
||||
}
|
||||
|
||||
currentFrame = m_downloadingMovie->currentPixmap();
|
||||
currentFrame = currentFrame.scaled(s_statusIconSize, s_statusIconSize);
|
||||
statusPixmap = ¤tFrame;
|
||||
}
|
||||
else if (downloadStatus == GemInfo::DownloadStatus::NotDownloaded)
|
||||
{
|
||||
statusPixmap = &m_notDownloadedPixmap;
|
||||
}
|
||||
else
|
||||
{
|
||||
statusPixmap = &m_unknownStatusPixmap;
|
||||
}
|
||||
|
||||
QSize statusSize = statusPixmap->size();
|
||||
|
||||
painter->drawPixmap(
|
||||
buttonRect.left() - s_statusButtonSpacing - statusSize.width(),
|
||||
contentRect.center().y() - statusSize.height() / 2,
|
||||
*statusPixmap);
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -49,13 +49,13 @@ namespace O3DE::ProjectManager
|
||||
|
||||
// Margin and borders
|
||||
inline constexpr static QMargins s_itemMargins = QMargins(/*left=*/16, /*top=*/8, /*right=*/16, /*bottom=*/8); // Item border distances
|
||||
inline constexpr static QMargins s_contentMargins = QMargins(/*left=*/20, /*top=*/12, /*right=*/15, /*bottom=*/12); // Distances of the elements within an item to the item borders
|
||||
inline constexpr static QMargins s_contentMargins = QMargins(/*left=*/20, /*top=*/12, /*right=*/20, /*bottom=*/12); // Distances of the elements within an item to the item borders
|
||||
inline constexpr static int s_borderWidth = 4;
|
||||
|
||||
// Button
|
||||
inline constexpr static int s_buttonWidth = 55;
|
||||
inline constexpr static int s_buttonHeight = 18;
|
||||
inline constexpr static int s_buttonBorderRadius = 9;
|
||||
inline constexpr static int s_buttonWidth = 32;
|
||||
inline constexpr static int s_buttonHeight = 16;
|
||||
inline constexpr static int s_buttonBorderRadius = s_buttonHeight / 2;
|
||||
inline constexpr static int s_buttonCircleRadius = s_buttonBorderRadius - 2;
|
||||
inline constexpr static qreal s_buttonFontSize = 10.0;
|
||||
|
||||
@@ -65,6 +65,9 @@ namespace O3DE::ProjectManager
|
||||
inline constexpr static int s_featureTagBorderMarginY = 3;
|
||||
inline constexpr static int s_featureTagSpacing = 7;
|
||||
|
||||
signals:
|
||||
void MovieStartedPlaying(const QMovie* playingMovie) const;
|
||||
|
||||
protected:
|
||||
bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) override;
|
||||
bool helpEvent(QHelpEvent* event, QAbstractItemView* view, const QStyleOptionViewItem& option, const QModelIndex& index) override;
|
||||
@@ -74,9 +77,10 @@ namespace O3DE::ProjectManager
|
||||
QRect CalcButtonRect(const QRect& contentRect) const;
|
||||
QRect CalcSummaryRect(const QRect& contentRect, bool hasTags) const;
|
||||
void DrawPlatformIcons(QPainter* painter, const QRect& contentRect, const QModelIndex& modelIndex) const;
|
||||
void DrawButton(QPainter* painter, const QRect& contentRect, const QModelIndex& modelIndex) const;
|
||||
void DrawButton(QPainter* painter, const QRect& buttonRect, const QModelIndex& modelIndex) const;
|
||||
void DrawFeatureTags(QPainter* painter, const QRect& contentRect, const QStringList& featureTags, const QFont& standardFont, const QRect& summaryRect) const;
|
||||
void DrawText(const QString& text, QPainter* painter, const QRect& rect, const QFont& standardFont) const;
|
||||
void DrawDownloadStatusIcon(QPainter* painter, const QRect& contentRect, const QRect& buttonRect, const QModelIndex& modelIndex) const;
|
||||
|
||||
QAbstractItemModel* m_model = nullptr;
|
||||
|
||||
@@ -85,5 +89,14 @@ namespace O3DE::ProjectManager
|
||||
void AddPlatformIcon(GemInfo::Platform platform, const QString& iconPath);
|
||||
inline constexpr static int s_platformIconSize = 12;
|
||||
QHash<GemInfo::Platform, QPixmap> m_platformIcons;
|
||||
|
||||
// Status icons
|
||||
void SetStatusIcon(QPixmap& m_iconPixmap, const QString& iconPath);
|
||||
inline constexpr static int s_statusIconSize = 16;
|
||||
inline constexpr static int s_statusButtonSpacing = 5;
|
||||
|
||||
QPixmap m_unknownStatusPixmap;
|
||||
QPixmap m_notDownloadedPixmap;
|
||||
QMovie* m_downloadingMovie = nullptr;
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -103,11 +103,11 @@ namespace O3DE::ProjectManager
|
||||
QSpacerItem* horizontalSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
columnHeaderLayout->addSpacerItem(horizontalSpacer);
|
||||
|
||||
QLabel* gemSelectedLabel = new QLabel(tr("Selected"));
|
||||
QLabel* gemSelectedLabel = new QLabel(tr("Status"));
|
||||
gemSelectedLabel->setObjectName("GemCatalogHeaderLabel");
|
||||
columnHeaderLayout->addWidget(gemSelectedLabel);
|
||||
|
||||
columnHeaderLayout->addSpacing(65);
|
||||
columnHeaderLayout->addSpacing(72);
|
||||
|
||||
vLayout->addLayout(columnHeaderLayout);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#include <GemCatalog/GemListView.h>
|
||||
#include <GemCatalog/GemItemDelegate.h>
|
||||
|
||||
#include <QMovie>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
GemListView::GemListView(QAbstractItemModel* model, QItemSelectionModel* selectionModel, QWidget* parent)
|
||||
@@ -19,6 +21,17 @@ namespace O3DE::ProjectManager
|
||||
|
||||
setModel(model);
|
||||
setSelectionModel(selectionModel);
|
||||
setItemDelegate(new GemItemDelegate(model, this));
|
||||
GemItemDelegate* itemDelegate = new GemItemDelegate(model, this);
|
||||
|
||||
connect(itemDelegate, &GemItemDelegate::MovieStartedPlaying, [=](const QMovie* playingMovie)
|
||||
{
|
||||
// Force redraw when movie is playing so animation is smooth
|
||||
connect(playingMovie, &QMovie::frameChanged, this, [=]
|
||||
{
|
||||
this->viewport()->repaint();
|
||||
});
|
||||
});
|
||||
|
||||
setItemDelegate(itemDelegate);
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -48,6 +48,7 @@ namespace O3DE::ProjectManager
|
||||
item->setData(gemInfo.m_features, RoleFeatures);
|
||||
item->setData(gemInfo.m_path, RolePath);
|
||||
item->setData(gemInfo.m_requirement, RoleRequirement);
|
||||
item->setData(gemInfo.m_downloadStatus, RoleDownloadStatus);
|
||||
|
||||
appendRow(item);
|
||||
|
||||
@@ -132,6 +133,11 @@ namespace O3DE::ProjectManager
|
||||
return static_cast<GemInfo::Types>(modelIndex.data(RoleTypes).toInt());
|
||||
}
|
||||
|
||||
GemInfo::DownloadStatus GemModel::GetDownloadStatus(const QModelIndex& modelIndex)
|
||||
{
|
||||
return static_cast<GemInfo::DownloadStatus>(modelIndex.data(RoleDownloadStatus).toInt());
|
||||
}
|
||||
|
||||
QString GemModel::GetSummary(const QModelIndex& modelIndex)
|
||||
{
|
||||
return modelIndex.data(RoleSummary).toString();
|
||||
@@ -373,6 +379,11 @@ namespace O3DE::ProjectManager
|
||||
return previouslyAdded && !added;
|
||||
}
|
||||
|
||||
void GemModel::SetDownloadStatus(QAbstractItemModel& model, const QModelIndex& modelIndex, GemInfo::DownloadStatus status)
|
||||
{
|
||||
model.setData(modelIndex, status, RoleDownloadStatus);
|
||||
}
|
||||
|
||||
bool GemModel::HasRequirement(const QModelIndex& modelIndex)
|
||||
{
|
||||
return !modelIndex.data(RoleRequirement).toString().isEmpty();
|
||||
|
||||
@@ -40,6 +40,7 @@ namespace O3DE::ProjectManager
|
||||
static GemInfo::GemOrigin GetGemOrigin(const QModelIndex& modelIndex);
|
||||
static GemInfo::Platforms GetPlatforms(const QModelIndex& modelIndex);
|
||||
static GemInfo::Types GetTypes(const QModelIndex& modelIndex);
|
||||
static GemInfo::DownloadStatus GetDownloadStatus(const QModelIndex& modelIndex);
|
||||
static QString GetSummary(const QModelIndex& modelIndex);
|
||||
static QString GetDirectoryLink(const QModelIndex& modelIndex);
|
||||
static QString GetDocLink(const QModelIndex& modelIndex);
|
||||
@@ -64,6 +65,7 @@ namespace O3DE::ProjectManager
|
||||
static bool NeedsToBeRemoved(const QModelIndex& modelIndex, bool includeDependencies = false);
|
||||
static bool HasRequirement(const QModelIndex& modelIndex);
|
||||
static void UpdateDependencies(QAbstractItemModel& model, const QModelIndex& modelIndex);
|
||||
static void SetDownloadStatus(QAbstractItemModel& model, const QModelIndex& modelIndex, GemInfo::DownloadStatus status);
|
||||
|
||||
bool DoGemsToBeAddedHaveRequirements() const;
|
||||
bool HasDependentGemsToRemove() const;
|
||||
@@ -101,7 +103,8 @@ namespace O3DE::ProjectManager
|
||||
RoleFeatures,
|
||||
RoleTypes,
|
||||
RolePath,
|
||||
RoleRequirement
|
||||
RoleRequirement,
|
||||
RoleDownloadStatus
|
||||
};
|
||||
|
||||
QHash<QString, QModelIndex> m_nameToIndexMap;
|
||||
|
||||
@@ -668,7 +668,21 @@ namespace O3DE::ProjectManager
|
||||
|
||||
if (gemInfo.m_creator.contains("Open 3D Engine"))
|
||||
{
|
||||
gemInfo.m_gemOrigin = GemInfo::GemOrigin::Open3DEEngine;
|
||||
gemInfo.m_gemOrigin = GemInfo::GemOrigin::Open3DEngine;
|
||||
}
|
||||
else if (gemInfo.m_creator.contains("Amazon Web Services"))
|
||||
{
|
||||
gemInfo.m_gemOrigin = GemInfo::GemOrigin::Local;
|
||||
}
|
||||
else if (data.contains("origin"))
|
||||
{
|
||||
gemInfo.m_gemOrigin = GemInfo::GemOrigin::Remote;
|
||||
}
|
||||
|
||||
// As long Base Open3DEngine gems are installed before first startup non-remote gems will be downloaded
|
||||
if (gemInfo.m_gemOrigin != GemInfo::GemOrigin::Remote)
|
||||
{
|
||||
gemInfo.m_downloadStatus = GemInfo::DownloadStatus::Downloaded;
|
||||
}
|
||||
|
||||
if (data.contains("user_tags"))
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace AWSClientAuth
|
||||
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serialize)
|
||||
{
|
||||
serialize->Class<AWSClientAuthSystemComponent, AZ::Component>()->Version(1);
|
||||
serialize->Class<AWSClientAuthSystemComponent, AZ::Component>()->Version(2);
|
||||
|
||||
if (AZ::EditContext* ec = serialize->GetEditContext())
|
||||
{
|
||||
@@ -105,12 +105,22 @@ namespace AWSClientAuth
|
||||
behaviorContext->EBus<AWSCognitoUserManagementRequestBus>("AWSCognitoUserManagementRequestBus")
|
||||
->Attribute(AZ::Script::Attributes::Category, SerializeComponentName)
|
||||
->Event("Initialize", &AWSCognitoUserManagementRequestBus::Events::Initialize)
|
||||
->Event("EmailSignUpAsync", &AWSCognitoUserManagementRequestBus::Events::EmailSignUpAsync)
|
||||
->Event("PhoneSignUpAsync", &AWSCognitoUserManagementRequestBus::Events::PhoneSignUpAsync)
|
||||
->Event("ConfirmSignUpAsync", &AWSCognitoUserManagementRequestBus::Events::ConfirmSignUpAsync)
|
||||
->Event("ForgotPasswordAsync", &AWSCognitoUserManagementRequestBus::Events::ForgotPasswordAsync)
|
||||
->Event("ConfirmForgotPasswordAsync", &AWSCognitoUserManagementRequestBus::Events::ConfirmForgotPasswordAsync)
|
||||
->Event("EnableMFAAsync", &AWSCognitoUserManagementRequestBus::Events::EnableMFAAsync);
|
||||
->Event(
|
||||
"EmailSignUpAsync", &AWSCognitoUserManagementRequestBus::Events::EmailSignUpAsync,
|
||||
{ { { "Username", "The client's username" }, { "Password", "The client's password" }, { "Email", "The email address used to sign up" } } })
|
||||
->Event(
|
||||
"PhoneSignUpAsync", &AWSCognitoUserManagementRequestBus::Events::PhoneSignUpAsync,
|
||||
{ { { "Username", "The client's username" }, { "Password", "The client's password" }, { "Phone number", "The phone number used to sign up" } } })
|
||||
->Event(
|
||||
"ConfirmSignUpAsync", &AWSCognitoUserManagementRequestBus::Events::ConfirmSignUpAsync,
|
||||
{ { { "Username", "The client's username" }, { "Confirmation code", "The client's confirmation code" } } })
|
||||
->Event(
|
||||
"ForgotPasswordAsync", &AWSCognitoUserManagementRequestBus::Events::ForgotPasswordAsync,
|
||||
{ { { "Username", "The client's username" } } })
|
||||
->Event(
|
||||
"ConfirmForgotPasswordAsync", &AWSCognitoUserManagementRequestBus::Events::ConfirmForgotPasswordAsync,
|
||||
{ { { "Username", "The client's username" }, { "Confirmation code", "The client's confirmation code" }, { "New password", "The new password for the client" } } })
|
||||
->Event("EnableMFAAsync", &AWSCognitoUserManagementRequestBus::Events::EnableMFAAsync, { { { "Access token", "The MFA access token" } } });
|
||||
|
||||
|
||||
behaviorContext->EBus<AuthenticationProviderNotificationBus>("AuthenticationProviderNotificationBus")
|
||||
|
||||
@@ -15,10 +15,11 @@ ly_add_target(
|
||||
awsgamelift_client_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PUBLIC
|
||||
../AWSGameLiftCommon/Include
|
||||
Include
|
||||
PRIVATE
|
||||
Source
|
||||
../AWSGameLiftCommon/Source
|
||||
Source
|
||||
COMPILE_DEFINITIONS
|
||||
PRIVATE
|
||||
${awsgameliftclient_compile_definition}
|
||||
@@ -78,10 +79,11 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
awsgamelift_client_tests_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PRIVATE
|
||||
../AWSGameLiftCommon/Include
|
||||
../AWSGameLiftCommon/Source
|
||||
Include
|
||||
Tests
|
||||
Source
|
||||
../AWSGameLiftCommon/Source
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
AZ::AzCore
|
||||
|
||||
+3
-27
@@ -10,36 +10,12 @@
|
||||
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
|
||||
#include <AzFramework/Matchmaking/MatchmakingRequests.h>
|
||||
|
||||
#include <AWSGameLiftPlayer.h>
|
||||
|
||||
namespace AWSGameLift
|
||||
{
|
||||
//! AWSGameLiftPlayerInformation
|
||||
//! Information on each player to be matched
|
||||
//! This information must include a player ID, and may contain player attributes and latency data to be used in the matchmaking process
|
||||
//! After a successful match, Player objects contain the name of the team the player is assigned to
|
||||
struct AWSGameLiftPlayerInformation
|
||||
{
|
||||
AZ_RTTI(AWSGameLiftPlayerInformation, "{B62C118E-C55D-4903-8ECB-E58E8CA613C4}");
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
AWSGameLiftPlayerInformation() = default;
|
||||
virtual ~AWSGameLiftPlayerInformation() = default;
|
||||
|
||||
// A map of region names to latencies in millseconds, that indicates
|
||||
// the amount of latency that a player experiences when connected to AWS Regions
|
||||
AZStd::unordered_map<AZStd::string, int> m_latencyInMs;
|
||||
// A collection of key:value pairs containing player information for use in matchmaking
|
||||
// Player attribute keys must match the playerAttributes used in a matchmaking rule set
|
||||
// Example: {"skill": "{\"N\": \"23\"}", "gameMode": "{\"S\": \"deathmatch\"}"}
|
||||
AZStd::unordered_map<AZStd::string, AZStd::string> m_playerAttributes;
|
||||
// A unique identifier for a player
|
||||
AZStd::string m_playerId;
|
||||
// Name of the team that the player is assigned to in a match
|
||||
AZStd::string m_team;
|
||||
};
|
||||
|
||||
//! AWSGameLiftStartMatchmakingRequest
|
||||
//! GameLift start matchmaking request which corresponds to Amazon GameLift
|
||||
//! Uses FlexMatch to create a game match for a group of players based on custom matchmaking rules
|
||||
@@ -57,6 +33,6 @@ namespace AWSGameLift
|
||||
// Name of the matchmaking configuration to use for this request
|
||||
AZStd::string m_configurationName;
|
||||
// Information on each player to be matched
|
||||
AZStd::vector<AWSGameLiftPlayerInformation> m_players;
|
||||
AZStd::vector<AWSGameLiftPlayer> m_players;
|
||||
};
|
||||
} // namespace AWSGameLift
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <AzCore/std/bind/bind.h>
|
||||
#include <AzCore/std/smart_ptr/shared_ptr.h>
|
||||
#include <AzFramework/Session/ISessionHandlingRequests.h>
|
||||
#include <AzFramework/Matchmaking/MatchmakingNotifications.h>
|
||||
|
||||
#include <AWSGameLiftClientLocalTicketTracker.h>
|
||||
#include <AWSGameLiftSessionConstants.h>
|
||||
@@ -109,6 +110,7 @@ namespace AWSGameLift
|
||||
else if (ticket.GetStatus() == Aws::GameLift::Model::MatchmakingConfigurationStatus::REQUIRES_ACCEPTANCE)
|
||||
{
|
||||
// broadcast acceptance requires to player
|
||||
AzFramework::MatchAcceptanceNotificationBus::Broadcast(&AzFramework::MatchAcceptanceNotifications::OnMatchAcceptance);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include <AWSGameLiftClientManager.h>
|
||||
#include <AWSGameLiftSessionConstants.h>
|
||||
#include <Activity/AWSGameLiftAcceptMatchActivity.h>
|
||||
#include <Activity/AWSGameLiftCreateSessionActivity.h>
|
||||
#include <Activity/AWSGameLiftCreateSessionOnQueueActivity.h>
|
||||
#include <Activity/AWSGameLiftJoinSessionActivity.h>
|
||||
@@ -125,12 +126,53 @@ namespace AWSGameLift
|
||||
|
||||
void AWSGameLiftClientManager::AcceptMatch(const AzFramework::AcceptMatchRequest& acceptMatchRequest)
|
||||
{
|
||||
AZ_UNUSED(acceptMatchRequest);
|
||||
if (AcceptMatchActivity::ValidateAcceptMatchRequest(acceptMatchRequest))
|
||||
{
|
||||
const AWSGameLiftAcceptMatchRequest& gameliftStartMatchmakingRequest =
|
||||
static_cast<const AWSGameLiftAcceptMatchRequest&>(acceptMatchRequest);
|
||||
AcceptMatchHelper(gameliftStartMatchmakingRequest);
|
||||
}
|
||||
}
|
||||
|
||||
void AWSGameLiftClientManager::AcceptMatchAsync(const AzFramework::AcceptMatchRequest& acceptMatchRequest)
|
||||
{
|
||||
AZ_UNUSED(acceptMatchRequest);
|
||||
if (!AcceptMatchActivity::ValidateAcceptMatchRequest(acceptMatchRequest))
|
||||
{
|
||||
AzFramework::MatchmakingAsyncRequestNotificationBus::Broadcast(
|
||||
&AzFramework::MatchmakingAsyncRequestNotifications::OnAcceptMatchAsyncComplete);
|
||||
return;
|
||||
}
|
||||
|
||||
const AWSGameLiftAcceptMatchRequest& gameliftStartMatchmakingRequest = static_cast<const AWSGameLiftAcceptMatchRequest&>(acceptMatchRequest);
|
||||
|
||||
AZ::JobContext* jobContext = nullptr;
|
||||
AWSCore::AWSCoreRequestBus::BroadcastResult(jobContext, &AWSCore::AWSCoreRequests::GetDefaultJobContext);
|
||||
AZ::Job* acceptMatchJob = AZ::CreateJobFunction(
|
||||
[this, gameliftStartMatchmakingRequest]()
|
||||
{
|
||||
AcceptMatchHelper(gameliftStartMatchmakingRequest);
|
||||
|
||||
AzFramework::MatchmakingAsyncRequestNotificationBus::Broadcast(
|
||||
&AzFramework::MatchmakingAsyncRequestNotifications::OnAcceptMatchAsyncComplete);
|
||||
},
|
||||
true, jobContext);
|
||||
|
||||
acceptMatchJob->Start();
|
||||
}
|
||||
|
||||
void AWSGameLiftClientManager::AcceptMatchHelper(const AWSGameLiftAcceptMatchRequest& acceptMatchRequest)
|
||||
{
|
||||
auto gameliftClient = AZ::Interface<IAWSGameLiftInternalRequests>::Get()->GetGameLiftClient();
|
||||
|
||||
AZStd::string response;
|
||||
if (!gameliftClient)
|
||||
{
|
||||
AZ_Error(AWSGameLiftClientManagerName, false, AWSGameLiftClientMissingErrorMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
AcceptMatchActivity::AcceptMatch(*gameliftClient, acceptMatchRequest);
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::string AWSGameLiftClientManager::CreateSession(const AzFramework::CreateSessionRequest& createSessionRequest)
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
namespace AWSGameLift
|
||||
{
|
||||
struct AWSGameLiftAcceptMatchRequest;
|
||||
struct AWSGameLiftCreateSessionRequest;
|
||||
struct AWSGameLiftCreateSessionOnQueueRequest;
|
||||
struct AWSGameLiftJoinSessionRequest;
|
||||
@@ -158,6 +159,7 @@ namespace AWSGameLift
|
||||
void LeaveSession() override;
|
||||
|
||||
private:
|
||||
void AcceptMatchHelper(const AWSGameLiftAcceptMatchRequest& createSessionRequest);
|
||||
AZStd::string CreateSessionHelper(const AWSGameLiftCreateSessionRequest& createSessionRequest);
|
||||
AZStd::string CreateSessionOnQueueHelper(const AWSGameLiftCreateSessionOnQueueRequest& createSessionOnQueueRequest);
|
||||
bool JoinSessionHelper(const AWSGameLiftJoinSessionRequest& joinSessionRequest);
|
||||
|
||||
@@ -210,6 +210,7 @@ namespace AWSGameLift
|
||||
->Property("SessionId", BehaviorValueProperty(&AzFramework::SessionConfig::m_sessionId))
|
||||
->Property("SessionName", BehaviorValueProperty(&AzFramework::SessionConfig::m_sessionName))
|
||||
->Property("SessionProperties", BehaviorValueProperty(&AzFramework::SessionConfig::m_sessionProperties))
|
||||
->Property("MatchmakingData", BehaviorValueProperty(&AzFramework::SessionConfig::m_matchmakingData))
|
||||
->Property("Status", BehaviorValueProperty(&AzFramework::SessionConfig::m_status))
|
||||
->Property("StatusReason", BehaviorValueProperty(&AzFramework::SessionConfig::m_statusReason))
|
||||
->Property("TerminationTime", BehaviorValueProperty(&AzFramework::SessionConfig::m_terminationTime))
|
||||
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.AcceptMatch
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
|
||||
#include <Activity/AWSGameLiftAcceptMatchActivity.h>
|
||||
#include <AWSGameLiftSessionConstants.h>
|
||||
|
||||
#include <aws/core/utils/Outcome.h>
|
||||
#include <aws/gamelift/model/AcceptMatchRequest.h>
|
||||
|
||||
namespace AWSGameLift
|
||||
{
|
||||
namespace AcceptMatchActivity
|
||||
{
|
||||
Aws::GameLift::Model::AcceptMatchRequest BuildAWSGameLiftAcceptMatchRequest(
|
||||
const AWSGameLiftAcceptMatchRequest& acceptMatchRequest)
|
||||
{
|
||||
Aws::GameLift::Model::AcceptMatchRequest request;
|
||||
request.SetAcceptanceType(acceptMatchRequest.m_acceptMatch ?
|
||||
Aws::GameLift::Model::AcceptanceType::ACCEPT : Aws::GameLift::Model::AcceptanceType::REJECT);
|
||||
|
||||
Aws::Vector<Aws::String> playerIds;
|
||||
for (const AZStd::string& playerId : acceptMatchRequest.m_playerIds)
|
||||
{
|
||||
playerIds.emplace_back(playerId.c_str());
|
||||
}
|
||||
request.SetPlayerIds(playerIds);
|
||||
|
||||
if (!acceptMatchRequest.m_ticketId.empty())
|
||||
{
|
||||
request.SetTicketId(acceptMatchRequest.m_ticketId.c_str());
|
||||
}
|
||||
|
||||
AZ_TracePrintf(AWSGameLiftAcceptMatchActivityName, "Built AcceptMatchRequest with TicketId=%s", request.GetTicketId().c_str());
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
void AcceptMatch(const Aws::GameLift::GameLiftClient& gameliftClient,
|
||||
const AWSGameLiftAcceptMatchRequest& AcceptMatchRequest)
|
||||
{
|
||||
AZ_TracePrintf(AWSGameLiftAcceptMatchActivityName, "Requesting AcceptMatch against Amazon GameLift service ...");
|
||||
|
||||
Aws::GameLift::Model::AcceptMatchRequest request = BuildAWSGameLiftAcceptMatchRequest(AcceptMatchRequest);
|
||||
auto AcceptMatchOutcome = gameliftClient.AcceptMatch(request);
|
||||
|
||||
if (AcceptMatchOutcome.IsSuccess())
|
||||
{
|
||||
AZ_TracePrintf(AWSGameLiftAcceptMatchActivityName, "AcceptMatch request against Amazon GameLift service is complete");
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error(AWSGameLiftAcceptMatchActivityName, false, AWSGameLiftErrorMessageTemplate,
|
||||
AcceptMatchOutcome.GetError().GetExceptionName().c_str(), AcceptMatchOutcome.GetError().GetMessage().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
bool ValidateAcceptMatchRequest(const AzFramework::AcceptMatchRequest& AcceptMatchRequest)
|
||||
{
|
||||
auto gameliftAcceptMatchRequest = azrtti_cast<const AWSGameLiftAcceptMatchRequest*>(&AcceptMatchRequest);
|
||||
bool isValid = gameliftAcceptMatchRequest &&
|
||||
(gameliftAcceptMatchRequest->m_playerIds.size() > 0) &&
|
||||
(!gameliftAcceptMatchRequest->m_ticketId.empty());
|
||||
|
||||
AZ_Error(AWSGameLiftAcceptMatchActivityName, isValid, AWSGameLiftAcceptMatchRequestInvalidErrorMessage);
|
||||
|
||||
return isValid;
|
||||
}
|
||||
} // namespace AcceptMatchActivity
|
||||
} // namespace AWSGameLift
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Request/AWSGameLiftAcceptMatchRequest.h>
|
||||
|
||||
#include <aws/gamelift/GameLiftClient.h>
|
||||
|
||||
namespace AWSGameLift
|
||||
{
|
||||
namespace AcceptMatchActivity
|
||||
{
|
||||
static constexpr const char AWSGameLiftAcceptMatchActivityName[] = "AWSGameLiftAcceptMatchActivity";
|
||||
static constexpr const char AWSGameLiftAcceptMatchRequestInvalidErrorMessage[] = "Invalid GameLift AcceptMatch request.";
|
||||
|
||||
// Build AWS GameLift AcceptMatchRequest by using AWSGameLiftAcceptMatchRequest
|
||||
Aws::GameLift::Model::AcceptMatchRequest BuildAWSGameLiftAcceptMatchRequest(const AWSGameLiftAcceptMatchRequest& AcceptMatchRequest);
|
||||
|
||||
// Create AcceptMatchRequest and make a AcceptMatch call through GameLift client
|
||||
void AcceptMatch(const Aws::GameLift::GameLiftClient& gameliftClient, const AWSGameLiftAcceptMatchRequest& AcceptMatchRequest);
|
||||
|
||||
// Validate AcceptMatchRequest and check required request parameters
|
||||
bool ValidateAcceptMatchRequest(const AzFramework::AcceptMatchRequest& AcceptMatchRequest);
|
||||
} // namespace AcceptMatchActivity
|
||||
} // namespace AWSGameLift
|
||||
+1
@@ -105,6 +105,7 @@ namespace AWSGameLift
|
||||
session.m_status = AWSGameLiftSessionStatusNames[(int)gameSession.GetStatus()];
|
||||
session.m_statusReason = AWSGameLiftSessionStatusReasons[(int)gameSession.GetStatusReason()];
|
||||
session.m_terminationTime = gameSession.GetTerminationTime().Millis();
|
||||
session.m_matchmakingData = gameSession.GetMatchmakerData().c_str();
|
||||
// TODO: Update the AWS Native SDK to get the new game session attributes.
|
||||
//session.m_dnsName = gameSession.GetDnsName();
|
||||
|
||||
|
||||
+7
-2
@@ -5,12 +5,17 @@
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
|
||||
#include <Activity/AWSGameLiftActivityUtils.h>
|
||||
#include <Activity/AWSGameLiftStartMatchmakingActivity.h>
|
||||
#include <AWSGameLiftPlayer.h>
|
||||
#include <AWSGameLiftSessionConstants.h>
|
||||
|
||||
#include <aws/core/utils/Outcome.h>
|
||||
#include <aws/gamelift/model/StartMatchmakingRequest.h>
|
||||
|
||||
namespace AWSGameLift
|
||||
{
|
||||
namespace StartMatchmakingActivity
|
||||
@@ -25,7 +30,7 @@ namespace AWSGameLift
|
||||
}
|
||||
|
||||
Aws::Vector<Aws::GameLift::Model::Player> players;
|
||||
for (const AWSGameLiftPlayerInformation& playerInfo : startMatchmakingRequest.m_players)
|
||||
for (const AWSGameLiftPlayer& playerInfo : startMatchmakingRequest.m_players)
|
||||
{
|
||||
Aws::GameLift::Model::Player player;
|
||||
if (!playerInfo.m_playerId.empty())
|
||||
@@ -105,7 +110,7 @@ namespace AWSGameLift
|
||||
|
||||
if (isValid)
|
||||
{
|
||||
for (const AWSGameLiftPlayerInformation& playerInfo : gameliftStartMatchmakingRequest->m_players)
|
||||
for (const AWSGameLiftPlayer& playerInfo : gameliftStartMatchmakingRequest->m_players)
|
||||
{
|
||||
isValid &= !playerInfo.m_playerId.empty();
|
||||
isValid &= AWSGameLiftActivityUtils::ValidatePlayerAttributes(playerInfo.m_playerAttributes);
|
||||
|
||||
-3
@@ -10,9 +10,7 @@
|
||||
|
||||
#include <Request/AWSGameLiftStartMatchmakingRequest.h>
|
||||
|
||||
#include <aws/core/utils/Outcome.h>
|
||||
#include <aws/gamelift/GameLiftClient.h>
|
||||
#include <aws/gamelift/model/StartMatchmakingRequest.h>
|
||||
|
||||
namespace AWSGameLift
|
||||
{
|
||||
@@ -25,7 +23,6 @@ namespace AWSGameLift
|
||||
Aws::GameLift::Model::StartMatchmakingRequest BuildAWSGameLiftStartMatchmakingRequest(const AWSGameLiftStartMatchmakingRequest& startMatchmakingRequest);
|
||||
|
||||
// Create StartMatchmakingRequest and make a StartMatchmaking call through GameLift client
|
||||
// Will also start polling the matchmaking ticket when get success outcome from GameLift client
|
||||
AZStd::string StartMatchmaking(const Aws::GameLift::GameLiftClient& gameliftClient, const AWSGameLiftStartMatchmakingRequest& startMatchmakingRequest);
|
||||
|
||||
// Validate StartMatchmakingRequest and check required request parameters
|
||||
|
||||
+3
@@ -11,6 +11,9 @@
|
||||
#include <Activity/AWSGameLiftStopMatchmakingActivity.h>
|
||||
#include <AWSGameLiftSessionConstants.h>
|
||||
|
||||
#include <aws/core/utils/Outcome.h>
|
||||
#include <aws/gamelift/model/StopMatchmakingRequest.h>
|
||||
|
||||
namespace AWSGameLift
|
||||
{
|
||||
namespace StopMatchmakingActivity
|
||||
|
||||
-3
@@ -10,9 +10,7 @@
|
||||
|
||||
#include <Request/AWSGameLiftStopMatchmakingRequest.h>
|
||||
|
||||
#include <aws/core/utils/Outcome.h>
|
||||
#include <aws/gamelift/GameLiftClient.h>
|
||||
#include <aws/gamelift/model/StopMatchmakingRequest.h>
|
||||
|
||||
namespace AWSGameLift
|
||||
{
|
||||
@@ -25,7 +23,6 @@ namespace AWSGameLift
|
||||
Aws::GameLift::Model::StopMatchmakingRequest BuildAWSGameLiftStopMatchmakingRequest(const AWSGameLiftStopMatchmakingRequest& stopMatchmakingRequest);
|
||||
|
||||
// Create StopMatchmakingRequest and make a StopMatchmaking call through GameLift client
|
||||
// Will also stop polling the matchmaking ticket when get success outcome from GameLift client
|
||||
void StopMatchmaking(const Aws::GameLift::GameLiftClient& gameliftClient, const AWSGameLiftStopMatchmakingRequest& stopMatchmakingRequest);
|
||||
|
||||
// Validate StopMatchmakingRequest and check required request parameters
|
||||
|
||||
+1
-44
@@ -14,53 +14,10 @@
|
||||
|
||||
namespace AWSGameLift
|
||||
{
|
||||
void AWSGameLiftPlayerInformation::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<AWSGameLiftPlayerInformation>()
|
||||
->Version(0)
|
||||
->Field("latencyInMs", &AWSGameLiftPlayerInformation::m_latencyInMs)
|
||||
->Field("playerAttributes", &AWSGameLiftPlayerInformation::m_playerAttributes)
|
||||
->Field("playerId", &AWSGameLiftPlayerInformation::m_playerId)
|
||||
->Field("team", &AWSGameLiftPlayerInformation::m_team);
|
||||
|
||||
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
|
||||
{
|
||||
editContext->Class<AWSGameLiftPlayerInformation>("AWSGameLiftPlayerInformation", "")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::Default, &AWSGameLiftPlayerInformation::m_latencyInMs, "LatencyInMs",
|
||||
"A set of values, expressed in milliseconds, that indicates the amount of latency that"
|
||||
"a player experiences when connected to AWS Regions")
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::Default, &AWSGameLiftPlayerInformation::m_playerAttributes, "PlayerAttributes",
|
||||
"A collection of key:value pairs containing player information for use in matchmaking")
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::Default, &AWSGameLiftPlayerInformation::m_playerId, "PlayerId",
|
||||
"A unique identifier for a player")
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::Default, &AWSGameLiftPlayerInformation::m_team, "Team",
|
||||
"Name of the team that the player is assigned to in a match");
|
||||
}
|
||||
}
|
||||
|
||||
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->Class<AWSGameLiftPlayerInformation>("AWSGameLiftPlayerInformation")
|
||||
->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value)
|
||||
->Property("LatencyInMs", BehaviorValueProperty(&AWSGameLiftPlayerInformation::m_latencyInMs))
|
||||
->Property("PlayerAttributes", BehaviorValueProperty(&AWSGameLiftPlayerInformation::m_playerAttributes))
|
||||
->Property("PlayerId", BehaviorValueProperty(&AWSGameLiftPlayerInformation::m_playerId))
|
||||
->Property("Team", BehaviorValueProperty(&AWSGameLiftPlayerInformation::m_team));
|
||||
}
|
||||
}
|
||||
|
||||
void AWSGameLiftStartMatchmakingRequest::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
AzFramework::StartMatchmakingRequest::Reflect(context);
|
||||
AWSGameLiftPlayerInformation::Reflect(context);
|
||||
AWSGameLiftPlayer::Reflect(context);
|
||||
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
|
||||
+38
@@ -351,3 +351,41 @@ TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallAndTicketComple
|
||||
WaitForProcessFinish();
|
||||
ASSERT_TRUE(m_gameliftClientTicketTracker->IsTrackerIdle());
|
||||
}
|
||||
|
||||
TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_RequiresAcceptanceAndTicketCompleteAtLast_ProcessContinuesAndStop)
|
||||
{
|
||||
Aws::GameLift::Model::MatchmakingTicket ticket1;
|
||||
ticket1.SetStatus(Aws::GameLift::Model::MatchmakingConfigurationStatus::REQUIRES_ACCEPTANCE);
|
||||
|
||||
Aws::GameLift::Model::DescribeMatchmakingResult result1;
|
||||
result1.AddTicketList(ticket1);
|
||||
Aws::GameLift::Model::DescribeMatchmakingOutcome outcome1(result1);
|
||||
|
||||
Aws::GameLift::Model::GameSessionConnectionInfo connectionInfo;
|
||||
connectionInfo.SetIpAddress("DummyIpAddress");
|
||||
connectionInfo.SetPort(123);
|
||||
connectionInfo.AddMatchedPlayerSessions(
|
||||
Aws::GameLift::Model::MatchedPlayerSession().WithPlayerId("player1").WithPlayerSessionId("playersession1"));
|
||||
|
||||
Aws::GameLift::Model::MatchmakingTicket ticket2;
|
||||
ticket2.SetStatus(Aws::GameLift::Model::MatchmakingConfigurationStatus::COMPLETED);
|
||||
ticket2.SetGameSessionConnectionInfo(connectionInfo);
|
||||
|
||||
Aws::GameLift::Model::DescribeMatchmakingResult result2;
|
||||
result2.AddTicketList(ticket2);
|
||||
Aws::GameLift::Model::DescribeMatchmakingOutcome outcome2(result2);
|
||||
|
||||
EXPECT_CALL(*m_gameliftClientMockPtr, DescribeMatchmaking(::testing::_))
|
||||
.WillOnce(::testing::Return(outcome1))
|
||||
.WillOnce(::testing::Return(outcome2));
|
||||
|
||||
MatchAcceptanceNotificationsHandlerMock handlerMock1;
|
||||
EXPECT_CALL(handlerMock1, OnMatchAcceptance()).Times(1);
|
||||
|
||||
SessionHandlingClientRequestsMock handlerMock2;
|
||||
EXPECT_CALL(handlerMock2, RequestPlayerJoinSession(::testing::_)).Times(1).WillOnce(::testing::Return(true));
|
||||
|
||||
m_gameliftClientTicketTracker->StartPolling("ticket1", "player1");
|
||||
WaitForProcessFinish();
|
||||
ASSERT_TRUE(m_gameliftClientTicketTracker->IsTrackerIdle());
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <AWSGameLiftClientManager.h>
|
||||
#include <AWSGameLiftClientMocks.h>
|
||||
|
||||
#include <Request/AWSGameLiftAcceptMatchRequest.h>
|
||||
#include <Request/AWSGameLiftCreateSessionOnQueueRequest.h>
|
||||
#include <Request/AWSGameLiftCreateSessionRequest.h>
|
||||
#include <Request/AWSGameLiftJoinSessionRequest.h>
|
||||
@@ -207,6 +208,7 @@ protected:
|
||||
sessionConfig.m_terminationTime = 0;
|
||||
sessionConfig.m_creatorId = "dummyCreatorId";
|
||||
sessionConfig.m_sessionProperties["dummyKey"] = "dummyValue";
|
||||
sessionConfig.m_matchmakingData = "dummyMatchmakingData";
|
||||
sessionConfig.m_sessionId = "dummyGameSessionId";
|
||||
sessionConfig.m_sessionName = "dummyGameSessionName";
|
||||
sessionConfig.m_ipAddress = "dummyIpAddress";
|
||||
@@ -231,7 +233,7 @@ protected:
|
||||
request.m_configurationName = "dummyConfiguration";
|
||||
request.m_ticketId = DummyMatchmakingTicketId;
|
||||
|
||||
AWSGameLiftPlayerInformation player;
|
||||
AWSGameLiftPlayer player;
|
||||
player.m_playerAttributes["dummy"] = "{\"N\": \"1\"}";
|
||||
player.m_playerId = DummyPlayerId;
|
||||
player.m_latencyInMs["us-east-1"] = 10;
|
||||
@@ -812,7 +814,7 @@ TEST_F(AWSGameLiftClientManagerTest, StartMatchmaking_CallWithInvalidRequest_Get
|
||||
{
|
||||
AWSGameLiftStartMatchmakingRequest request;
|
||||
request.m_configurationName = "dummyConfiguration";
|
||||
AWSGameLiftPlayerInformation player;
|
||||
AWSGameLiftPlayer player;
|
||||
player.m_playerAttributes["dummy"] = "{\"A\": \"1\"}";
|
||||
request.m_players.emplace_back(player);
|
||||
|
||||
@@ -854,7 +856,7 @@ TEST_F(AWSGameLiftClientManagerTest, StartMatchmakingAsync_CallWithInvalidReques
|
||||
{
|
||||
AWSGameLiftStartMatchmakingRequest request;
|
||||
request.m_configurationName = "dummyConfiguration";
|
||||
AWSGameLiftPlayerInformation player;
|
||||
AWSGameLiftPlayer player;
|
||||
player.m_playerAttributes["dummy"] = "{\"A\": \"1\"}";
|
||||
request.m_players.emplace_back(player);
|
||||
|
||||
@@ -1005,3 +1007,106 @@ TEST_F(AWSGameLiftClientManagerTest, StopMatchmakingAsync_CallWithValidRequest_G
|
||||
m_gameliftClientManager->StopMatchmakingAsync(request);
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message
|
||||
}
|
||||
|
||||
TEST_F(AWSGameLiftClientManagerTest, AcceptMatch_CallWithoutClientSetup_GetError)
|
||||
{
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
m_gameliftClientManager->ConfigureGameLiftClient("");
|
||||
AWSGameLiftAcceptMatchRequest request;
|
||||
request.m_acceptMatch = true;
|
||||
request.m_playerIds = { DummyPlayerId };
|
||||
request.m_ticketId = DummyMatchmakingTicketId;
|
||||
|
||||
m_gameliftClientManager->AcceptMatch(request);
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(2); // capture 2 error message
|
||||
}
|
||||
TEST_F(AWSGameLiftClientManagerTest, AcceptMatch_CallWithInvalidRequest_GetError)
|
||||
{
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
m_gameliftClientManager->AcceptMatch(AzFramework::AcceptMatchRequest());
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message
|
||||
}
|
||||
|
||||
TEST_F(AWSGameLiftClientManagerTest, AcceptMatch_CallWithValidRequest_Success)
|
||||
{
|
||||
AWSGameLiftAcceptMatchRequest request;
|
||||
request.m_acceptMatch = true;
|
||||
request.m_playerIds = { DummyPlayerId };
|
||||
request.m_ticketId = DummyMatchmakingTicketId;
|
||||
|
||||
Aws::GameLift::Model::AcceptMatchResult result;
|
||||
Aws::GameLift::Model::AcceptMatchResult outcome(result);
|
||||
EXPECT_CALL(*m_gameliftClientMockPtr, AcceptMatch(::testing::_)).Times(1).WillOnce(::testing::Return(outcome));
|
||||
|
||||
m_gameliftClientManager->AcceptMatch(request);
|
||||
}
|
||||
|
||||
TEST_F(AWSGameLiftClientManagerTest, AcceptMatch_CallWithValidRequest_GetError)
|
||||
{
|
||||
AWSGameLiftAcceptMatchRequest request;
|
||||
request.m_acceptMatch = true;
|
||||
request.m_playerIds = { DummyPlayerId };
|
||||
request.m_ticketId = DummyMatchmakingTicketId;
|
||||
|
||||
Aws::Client::AWSError<Aws::GameLift::GameLiftErrors> error;
|
||||
Aws::GameLift::Model::AcceptMatchOutcome outcome(error);
|
||||
|
||||
EXPECT_CALL(*m_gameliftClientMockPtr, AcceptMatch(::testing::_)).Times(1).WillOnce(::testing::Return(outcome));
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
m_gameliftClientManager->AcceptMatch(request);
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message
|
||||
}
|
||||
|
||||
TEST_F(AWSGameLiftClientManagerTest, AcceptMatchAsync_CallWithInvalidRequest_GetNotificationWithError)
|
||||
{
|
||||
AWSGameLiftAcceptMatchRequest request;
|
||||
|
||||
MatchmakingAsyncRequestNotificationsHandlerMock matchmakingHandlerMock;
|
||||
EXPECT_CALL(matchmakingHandlerMock, OnAcceptMatchAsyncComplete()).Times(1);
|
||||
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
m_gameliftClientManager->AcceptMatchAsync(request);
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message
|
||||
}
|
||||
|
||||
TEST_F(AWSGameLiftClientManagerTest, AcceptMatchAsync_CallWithValidRequest_GetNotification)
|
||||
{
|
||||
AWSCoreRequestsHandlerMock handlerMock;
|
||||
EXPECT_CALL(handlerMock, GetDefaultJobContext()).Times(1).WillOnce(::testing::Return(m_jobContext.get()));
|
||||
|
||||
AWSGameLiftAcceptMatchRequest request;
|
||||
request.m_acceptMatch = true;
|
||||
request.m_playerIds = { DummyPlayerId };
|
||||
request.m_ticketId = DummyMatchmakingTicketId;
|
||||
|
||||
Aws::GameLift::Model::AcceptMatchResult result;
|
||||
Aws::GameLift::Model::AcceptMatchOutcome outcome(result);
|
||||
EXPECT_CALL(*m_gameliftClientMockPtr, AcceptMatch(::testing::_)).Times(1).WillOnce(::testing::Return(outcome));
|
||||
|
||||
MatchmakingAsyncRequestNotificationsHandlerMock matchmakingHandlerMock;
|
||||
EXPECT_CALL(matchmakingHandlerMock, OnAcceptMatchAsyncComplete()).Times(1);
|
||||
|
||||
m_gameliftClientManager->AcceptMatchAsync(request);
|
||||
}
|
||||
|
||||
TEST_F(AWSGameLiftClientManagerTest, AcceptMatchAsync_CallWithValidRequest_GetNotificationWithError)
|
||||
{
|
||||
AWSCoreRequestsHandlerMock handlerMock;
|
||||
EXPECT_CALL(handlerMock, GetDefaultJobContext()).Times(1).WillOnce(::testing::Return(m_jobContext.get()));
|
||||
|
||||
AWSGameLiftAcceptMatchRequest request;
|
||||
request.m_acceptMatch = true;
|
||||
request.m_playerIds = { DummyPlayerId };
|
||||
request.m_ticketId = DummyMatchmakingTicketId;
|
||||
|
||||
Aws::Client::AWSError<Aws::GameLift::GameLiftErrors> error;
|
||||
Aws::GameLift::Model::AcceptMatchOutcome outcome(error);
|
||||
EXPECT_CALL(*m_gameliftClientMockPtr, AcceptMatch(::testing::_)).Times(1).WillOnce(::testing::Return(outcome));
|
||||
|
||||
MatchmakingAsyncRequestNotificationsHandlerMock matchmakingHandlerMock;
|
||||
EXPECT_CALL(matchmakingHandlerMock, OnAcceptMatchAsyncComplete()).Times(1);
|
||||
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
m_gameliftClientManager->AcceptMatchAsync(request);
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzFramework/Matchmaking/MatchmakingNotifications.h>
|
||||
#include <AzFramework/Session/ISessionRequests.h>
|
||||
#include <AzFramework/Session/ISessionHandlingRequests.h>
|
||||
#include <AzFramework/Matchmaking/MatchmakingNotifications.h>
|
||||
@@ -18,6 +19,8 @@
|
||||
#include <aws/core/utils/Outcome.h>
|
||||
#include <aws/gamelift/GameLiftClient.h>
|
||||
#include <aws/gamelift/GameLiftErrors.h>
|
||||
#include <aws/gamelift/model/AcceptMatchRequest.h>
|
||||
#include <aws/gamelift/model/AcceptMatchResult.h>
|
||||
#include <aws/gamelift/model/CreateGameSessionRequest.h>
|
||||
#include <aws/gamelift/model/CreateGameSessionResult.h>
|
||||
#include <aws/gamelift/model/CreatePlayerSessionRequest.h>
|
||||
@@ -46,6 +49,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
MOCK_CONST_METHOD1(AcceptMatch, Model::AcceptMatchOutcome(const Model::AcceptMatchRequest&));
|
||||
MOCK_CONST_METHOD1(CreateGameSession, Model::CreateGameSessionOutcome(const Model::CreateGameSessionRequest&));
|
||||
MOCK_CONST_METHOD1(CreatePlayerSession, Model::CreatePlayerSessionOutcome(const Model::CreatePlayerSessionRequest&));
|
||||
MOCK_CONST_METHOD1(DescribeMatchmaking, Model::DescribeMatchmakingOutcome(const Model::DescribeMatchmakingRequest&));
|
||||
@@ -74,6 +78,23 @@ public:
|
||||
MOCK_METHOD0(OnStopMatchmakingAsyncComplete, void());
|
||||
};
|
||||
|
||||
class MatchAcceptanceNotificationsHandlerMock
|
||||
: public AzFramework::MatchAcceptanceNotificationBus::Handler
|
||||
{
|
||||
public:
|
||||
MatchAcceptanceNotificationsHandlerMock()
|
||||
{
|
||||
AzFramework::MatchAcceptanceNotificationBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
~MatchAcceptanceNotificationsHandlerMock()
|
||||
{
|
||||
AzFramework::MatchAcceptanceNotificationBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
MOCK_METHOD0(OnMatchAcceptance, void());
|
||||
};
|
||||
|
||||
class SessionAsyncRequestNotificationsHandlerMock
|
||||
: public AzFramework::SessionAsyncRequestNotificationBus::Handler
|
||||
{
|
||||
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <aws/gamelift/model/AcceptMatchRequest.h>
|
||||
|
||||
#include <AWSGameLiftClientFixture.h>
|
||||
#include <Activity/AWSGameLiftAcceptMatchActivity.h>
|
||||
|
||||
#include <aws/gamelift/model/AcceptMatchRequest.h>
|
||||
|
||||
using namespace AWSGameLift;
|
||||
|
||||
using AWSGameLiftAcceptMatchActivityTest = AWSGameLiftClientFixture;
|
||||
|
||||
TEST_F(AWSGameLiftAcceptMatchActivityTest, BuildAWSGameLiftAcceptMatchRequest_Call_GetExpectedResult)
|
||||
{
|
||||
AWSGameLiftAcceptMatchRequest request;
|
||||
request.m_acceptMatch = true;
|
||||
request.m_ticketId = "dummyTicketId";
|
||||
request.m_playerIds = { "dummyPlayerId" };
|
||||
|
||||
auto awsRequest = AcceptMatchActivity::BuildAWSGameLiftAcceptMatchRequest(request);
|
||||
|
||||
EXPECT_EQ(awsRequest.GetAcceptanceType(), Aws::GameLift::Model::AcceptanceType::ACCEPT);
|
||||
EXPECT_TRUE(strcmp(awsRequest.GetTicketId().c_str(), request.m_ticketId.c_str()) == 0);
|
||||
EXPECT_EQ(awsRequest.GetPlayerIds().size(), request.m_playerIds.size());
|
||||
EXPECT_TRUE(strcmp(awsRequest.GetPlayerIds().begin()->c_str(), request.m_playerIds.begin()->c_str()) == 0);
|
||||
}
|
||||
|
||||
TEST_F(AWSGameLiftAcceptMatchActivityTest, ValidateAcceptMatchRequest_CallWithBaseType_GetFalseResult)
|
||||
{
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
auto result = AcceptMatchActivity::ValidateAcceptMatchRequest(AzFramework::AcceptMatchRequest());
|
||||
EXPECT_FALSE(result);
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message
|
||||
}
|
||||
|
||||
TEST_F(AWSGameLiftAcceptMatchActivityTest, ValidateAcceptMatchRequest_CallWithoutTicketId_GetFalseResult)
|
||||
{
|
||||
AWSGameLiftAcceptMatchRequest request;
|
||||
request.m_acceptMatch = true;
|
||||
request.m_playerIds = { "dummyPlayerId" };
|
||||
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
auto result = AcceptMatchActivity::ValidateAcceptMatchRequest(request);
|
||||
EXPECT_FALSE(result);
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message
|
||||
}
|
||||
|
||||
TEST_F(AWSGameLiftAcceptMatchActivityTest, ValidateAcceptMatchRequest_CallWithoutPlayerIds_GetFalseResult)
|
||||
{
|
||||
AWSGameLiftAcceptMatchRequest request;
|
||||
request.m_acceptMatch = true;
|
||||
request.m_playerIds = { "dummyPlayerId" };
|
||||
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
auto result = AcceptMatchActivity::ValidateAcceptMatchRequest(request);
|
||||
EXPECT_FALSE(result);
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message
|
||||
}
|
||||
|
||||
TEST_F(AWSGameLiftAcceptMatchActivityTest, ValidateAcceptMatchRequest_CallWithValidAttributes_GetTrueResult)
|
||||
{
|
||||
|
||||
AWSGameLiftAcceptMatchRequest request;
|
||||
request.m_acceptMatch = true;
|
||||
request.m_ticketId = "dummyTicketId";
|
||||
request.m_playerIds = { "dummyPlayerId" };
|
||||
|
||||
auto result = AcceptMatchActivity::ValidateAcceptMatchRequest(request);
|
||||
EXPECT_TRUE(result);
|
||||
}
|
||||
+9
-6
@@ -8,6 +8,9 @@
|
||||
|
||||
#include <Activity/AWSGameLiftStartMatchmakingActivity.h>
|
||||
#include <AWSGameLiftClientFixture.h>
|
||||
#include <AWSGameLiftPlayer.h>
|
||||
|
||||
#include <aws/gamelift/model/StartMatchmakingRequest.h>
|
||||
|
||||
using namespace AWSGameLift;
|
||||
|
||||
@@ -19,7 +22,7 @@ TEST_F(AWSGameLiftStartMatchmakingActivityTest, BuildAWSGameLiftStartMatchmaking
|
||||
request.m_configurationName = "dummyConfiguration";
|
||||
request.m_ticketId = "dummyTicketId";
|
||||
|
||||
AWSGameLiftPlayerInformation player;
|
||||
AWSGameLiftPlayer player;
|
||||
player.m_playerAttributes["dummy"] = "{\"S\": \"test\"}";
|
||||
player.m_playerId = "dummyPlayerId";
|
||||
player.m_team = "dummyTeam";
|
||||
@@ -56,7 +59,7 @@ TEST_F(AWSGameLiftStartMatchmakingActivityTest, ValidateStartMatchmakingRequest_
|
||||
AWSGameLiftStartMatchmakingRequest request;
|
||||
request.m_ticketId = "dummyTicketId";
|
||||
|
||||
AWSGameLiftPlayerInformation player;
|
||||
AWSGameLiftPlayer player;
|
||||
player.m_playerAttributes["dummy"] = "{\"S\": \"test\"}";
|
||||
player.m_playerId = "dummyPlayerId";
|
||||
player.m_team = "dummyTeam";
|
||||
@@ -87,7 +90,7 @@ TEST_F(AWSGameLiftStartMatchmakingActivityTest, ValidateStartMatchmakingRequest_
|
||||
request.m_configurationName = "dummyConfiguration";
|
||||
request.m_ticketId = "dummyTicketId";
|
||||
|
||||
AWSGameLiftPlayerInformation player;
|
||||
AWSGameLiftPlayer player;
|
||||
player.m_playerAttributes["dummy"] = "{\"S\": \"test\"}";
|
||||
player.m_team = "dummyTeam";
|
||||
player.m_latencyInMs["us-east-1"] = 10;
|
||||
@@ -105,7 +108,7 @@ TEST_F(AWSGameLiftStartMatchmakingActivityTest, ValidateStartMatchmakingRequest_
|
||||
request.m_configurationName = "dummyConfiguration";
|
||||
request.m_ticketId = "dummyTicketId";
|
||||
|
||||
AWSGameLiftPlayerInformation player;
|
||||
AWSGameLiftPlayer player;
|
||||
player.m_playerAttributes["dummy"] = "{\"A\": \"test\"}";
|
||||
player.m_playerId = "dummyPlayerId";
|
||||
player.m_team = "dummyTeam";
|
||||
@@ -123,7 +126,7 @@ TEST_F(AWSGameLiftStartMatchmakingActivityTest, ValidateStartMatchmakingRequest_
|
||||
AWSGameLiftStartMatchmakingRequest request;
|
||||
request.m_configurationName = "dummyConfiguration";
|
||||
|
||||
AWSGameLiftPlayerInformation player;
|
||||
AWSGameLiftPlayer player;
|
||||
player.m_playerAttributes["dummy"] = "{\"S\": \"test\"}";
|
||||
player.m_playerId = "dummyPlayerId";
|
||||
player.m_team = "dummyTeam";
|
||||
@@ -140,7 +143,7 @@ TEST_F(AWSGameLiftStartMatchmakingActivityTest, ValidateStartMatchmakingRequest_
|
||||
request.m_ticketId = "dummyTicketId";
|
||||
request.m_configurationName = "dummyConfiguration";
|
||||
|
||||
AWSGameLiftPlayerInformation player;
|
||||
AWSGameLiftPlayer player;
|
||||
player.m_playerAttributes["dummy"] = "{\"S\": \"test\"}";
|
||||
player.m_playerId = "dummyPlayerId";
|
||||
player.m_team = "dummyTeam";
|
||||
|
||||
+2
@@ -9,6 +9,8 @@
|
||||
#include <Activity/AWSGameLiftStopMatchmakingActivity.h>
|
||||
#include <AWSGameLiftClientFixture.h>
|
||||
|
||||
#include <aws/gamelift/model/StopMatchmakingRequest.h>
|
||||
|
||||
using namespace AWSGameLift;
|
||||
|
||||
using AWSGameLiftStopMatchmakingActivityTest = AWSGameLiftClientFixture;
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#
|
||||
|
||||
set(FILES
|
||||
../AWSGameLiftCommon/Include/AWSGameLiftPlayer.h
|
||||
../AWSGameLiftCommon/Source/AWSGameLiftPlayer.cpp
|
||||
../AWSGameLiftCommon/Source/AWSGameLiftSessionConstants.h
|
||||
Include/Request/AWSGameLiftAcceptMatchRequest.h
|
||||
Include/Request/AWSGameLiftCreateSessionOnQueueRequest.h
|
||||
@@ -18,6 +20,8 @@ set(FILES
|
||||
Include/Request/IAWSGameLiftRequests.h
|
||||
Source/Activity/AWSGameLiftActivityUtils.cpp
|
||||
Source/Activity/AWSGameLiftActivityUtils.h
|
||||
Source/Activity/AWSGameLiftAcceptMatchActivity.cpp
|
||||
Source/Activity/AWSGameLiftAcceptMatchActivity.h
|
||||
Source/Activity/AWSGameLiftCreateSessionActivity.cpp
|
||||
Source/Activity/AWSGameLiftCreateSessionActivity.h
|
||||
Source/Activity/AWSGameLiftCreateSessionOnQueueActivity.cpp
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#
|
||||
|
||||
set(FILES
|
||||
Tests/Activity/AWSGameLiftAcceptMatchActivityTest.cpp
|
||||
Tests/Activity/AWSGameLiftCreateSessionActivityTest.cpp
|
||||
Tests/Activity/AWSGameLiftCreateSessionOnQueueActivityTest.cpp
|
||||
Tests/Activity/AWSGameLiftJoinSessionActivityTest.cpp
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
|
||||
namespace AWSGameLift
|
||||
{
|
||||
//! AWSGameLiftPlayer
|
||||
//! Information on each player to be matched
|
||||
//! This information must include a player ID, and may contain player attributes and latency data to be used in the matchmaking process
|
||||
//! After a successful match, Player objects contain the name of the team the player is assigned to
|
||||
struct AWSGameLiftPlayer
|
||||
{
|
||||
AZ_RTTI(AWSGameLiftPlayer, "{B62C118E-C55D-4903-8ECB-E58E8CA613C4}");
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
AWSGameLiftPlayer() = default;
|
||||
virtual ~AWSGameLiftPlayer() = default;
|
||||
|
||||
// A map of region names to latencies in millseconds, that indicates
|
||||
// the amount of latency that a player experiences when connected to AWS Regions
|
||||
AZStd::unordered_map<AZStd::string, int> m_latencyInMs;
|
||||
|
||||
// A collection of key:value pairs containing player information for use in matchmaking
|
||||
// Player attribute keys must match the playerAttributes used in a matchmaking rule set
|
||||
// Example: {"skill": "{\"N\": 23}", "gameMode": "{\"S\": \"deathmatch\"}"}
|
||||
AZStd::unordered_map<AZStd::string, AZStd::string> m_playerAttributes;
|
||||
|
||||
// A unique identifier for a player
|
||||
AZStd::string m_playerId;
|
||||
|
||||
// Name of the team that the player is assigned to in a match
|
||||
AZStd::string m_team;
|
||||
};
|
||||
} // namespace AWSGameLift
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
#include <AWSGameLiftPlayer.h>
|
||||
|
||||
namespace AWSGameLift
|
||||
{
|
||||
void AWSGameLiftPlayer::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<AWSGameLiftPlayer>()
|
||||
->Version(0)
|
||||
->Field("latencyInMs", &AWSGameLiftPlayer::m_latencyInMs)
|
||||
->Field("playerAttributes", &AWSGameLiftPlayer::m_playerAttributes)
|
||||
->Field("playerId", &AWSGameLiftPlayer::m_playerId)
|
||||
->Field("team", &AWSGameLiftPlayer::m_team);
|
||||
|
||||
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
|
||||
{
|
||||
editContext->Class<AWSGameLiftPlayer>("AWSGameLiftPlayer", "")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::Default, &AWSGameLiftPlayer::m_latencyInMs, "LatencyInMs",
|
||||
"A set of values, expressed in milliseconds, that indicates the amount of latency that"
|
||||
"a player experiences when connected to AWS Regions")
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::Default, &AWSGameLiftPlayer::m_playerAttributes, "PlayerAttributes",
|
||||
"A collection of key:value pairs containing player information for use in matchmaking")
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::Default, &AWSGameLiftPlayer::m_playerId, "PlayerId",
|
||||
"A unique identifier for a player")
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::Default, &AWSGameLiftPlayer::m_team, "Team",
|
||||
"Name of the team that the player is assigned to in a match");
|
||||
}
|
||||
}
|
||||
|
||||
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->Class<AWSGameLiftPlayer>("AWSGameLiftPlayer")
|
||||
->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value)
|
||||
->Property("LatencyInMs", BehaviorValueProperty(&AWSGameLiftPlayer::m_latencyInMs))
|
||||
->Property("PlayerAttributes", BehaviorValueProperty(&AWSGameLiftPlayer::m_playerAttributes))
|
||||
->Property("PlayerId", BehaviorValueProperty(&AWSGameLiftPlayer::m_playerId))
|
||||
->Property("Team", BehaviorValueProperty(&AWSGameLiftPlayer::m_team));
|
||||
}
|
||||
}
|
||||
} // namespace AWSGameLift
|
||||
@@ -17,6 +17,7 @@ ly_add_target(
|
||||
awsgamelift_server_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PUBLIC
|
||||
../AWSGameLiftCommon/Include
|
||||
Include
|
||||
PRIVATE
|
||||
../AWSGameLiftCommon/Source
|
||||
@@ -54,6 +55,8 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
awsgamelift_server_tests_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PRIVATE
|
||||
../AWSGameLiftCommon/Include
|
||||
../AWSGameLiftCommon/Source
|
||||
Tests
|
||||
Source
|
||||
BUILD_DEPENDENCIES
|
||||
|
||||
+18
-3
@@ -9,9 +9,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/EBus/EBus.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzCore/RTTI/RTTI.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzFramework/Session/ISessionRequests.h>
|
||||
|
||||
#include <AWSGameLiftPlayer.h>
|
||||
|
||||
namespace AWSGameLift
|
||||
{
|
||||
@@ -26,8 +28,21 @@ namespace AWSGameLift
|
||||
virtual ~IAWSGameLiftServerRequests() = default;
|
||||
|
||||
//! Notify GameLift that the server process is ready to host a game session.
|
||||
//! @return Whether the ProcessReady notification is sent to GameLift.
|
||||
//! @return True if the ProcessReady notification is sent to GameLift successfully, false otherwise
|
||||
virtual bool NotifyGameLiftProcessReady() = 0;
|
||||
|
||||
//! Sends a request to find new players for open slots in a game session created with FlexMatch.
|
||||
//! @param ticketId Unique identifier for match backfill request ticket
|
||||
//! @param players A set of data representing all players who are currently in the game session,
|
||||
//! if not provided, system will use lazy loaded game session data which is not guaranteed to
|
||||
//! be accurate (no latency data either)
|
||||
//! @return True if StartMatchBackfill succeeds, false otherwise
|
||||
virtual bool StartMatchBackfill(const AZStd::string& ticketId, const AZStd::vector<AWSGameLiftPlayer>& players) = 0;
|
||||
|
||||
//! Cancels an active match backfill request that was created with StartMatchBackfill
|
||||
//! @param ticketId Unique identifier of the backfill request ticket to be canceled
|
||||
//! @return True if StopMatchBackfill succeeds, false otherwise
|
||||
virtual bool StopMatchBackfill(const AZStd::string& ticketId) = 0;
|
||||
};
|
||||
|
||||
// IAWSGameLiftServerRequests EBus wrapper for scripting
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
#include <AzCore/Jobs/JobFunction.h>
|
||||
#include <AzCore/Jobs/JobManagerBus.h>
|
||||
#include <AzCore/JSON/error/en.h>
|
||||
#include <AzCore/JSON/stringbuffer.h>
|
||||
#include <AzCore/JSON/writer.h>
|
||||
#include <AzCore/std/bind/bind.h>
|
||||
#include <AzFramework/Session/SessionNotifications.h>
|
||||
|
||||
@@ -112,6 +115,7 @@ namespace AWSGameLift
|
||||
{
|
||||
propertiesOutput = propertiesOutput.substr(0, propertiesOutput.size() - 1); // Trim last comma to fit array format
|
||||
}
|
||||
sessionConfig.m_matchmakingData = gameSession.GetMatchmakerData().c_str();
|
||||
sessionConfig.m_sessionId = gameSession.GetGameSessionId().c_str();
|
||||
sessionConfig.m_ipAddress = gameSession.GetIpAddress().c_str();
|
||||
sessionConfig.m_maxPlayer = gameSession.GetMaximumPlayerSessionCount();
|
||||
@@ -133,6 +137,276 @@ namespace AWSGameLift
|
||||
return sessionConfig;
|
||||
}
|
||||
|
||||
bool AWSGameLiftServerManager::BuildServerMatchBackfillPlayer(
|
||||
const AWSGameLiftPlayer& player, Aws::GameLift::Server::Model::Player& outBackfillPlayer)
|
||||
{
|
||||
outBackfillPlayer.SetPlayerId(player.m_playerId.c_str());
|
||||
outBackfillPlayer.SetTeam(player.m_team.c_str());
|
||||
for (auto latencyPair : player.m_latencyInMs)
|
||||
{
|
||||
outBackfillPlayer.AddLatencyInMs(latencyPair.first.c_str(), latencyPair.second);
|
||||
}
|
||||
|
||||
for (auto attributePair : player.m_playerAttributes)
|
||||
{
|
||||
Aws::GameLift::Server::Model::AttributeValue playerAttribute;
|
||||
rapidjson::Document attributeDocument;
|
||||
rapidjson::ParseResult parseResult = attributeDocument.Parse(attributePair.second.c_str());
|
||||
// player attribute json content should always be a single member object
|
||||
if (parseResult && attributeDocument.IsObject() && attributeDocument.MemberCount() == 1)
|
||||
{
|
||||
if ((attributeDocument.HasMember(AWSGameLiftMatchmakingPlayerAttributeSTypeName) ||
|
||||
attributeDocument.HasMember(AWSGameLiftMatchmakingPlayerAttributeSServerTypeName)) &&
|
||||
attributeDocument.MemberBegin()->value.IsString())
|
||||
{
|
||||
playerAttribute = Aws::GameLift::Server::Model::AttributeValue(
|
||||
attributeDocument.MemberBegin()->value.GetString());
|
||||
}
|
||||
else if ((attributeDocument.HasMember(AWSGameLiftMatchmakingPlayerAttributeNTypeName) ||
|
||||
attributeDocument.HasMember(AWSGameLiftMatchmakingPlayerAttributeNServerTypeName)) &&
|
||||
attributeDocument.MemberBegin()->value.IsNumber())
|
||||
{
|
||||
playerAttribute = Aws::GameLift::Server::Model::AttributeValue(
|
||||
attributeDocument.MemberBegin()->value.GetDouble());
|
||||
}
|
||||
else if ((attributeDocument.HasMember(AWSGameLiftMatchmakingPlayerAttributeSDMTypeName) ||
|
||||
attributeDocument.HasMember(AWSGameLiftMatchmakingPlayerAttributeSDMServerTypeName)) &&
|
||||
attributeDocument.MemberBegin()->value.IsObject())
|
||||
{
|
||||
playerAttribute = Aws::GameLift::Server::Model::AttributeValue::ConstructStringDoubleMap();
|
||||
for (auto iter = attributeDocument.MemberBegin()->value.MemberBegin();
|
||||
iter != attributeDocument.MemberBegin()->value.MemberEnd(); iter++)
|
||||
{
|
||||
if (iter->name.IsString() && iter->value.IsNumber())
|
||||
{
|
||||
playerAttribute.AddStringAndDouble(iter->name.GetString(), iter->value.GetDouble());
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftMatchmakingPlayerAttributeInvalidErrorMessage,
|
||||
player.m_playerId.c_str(), "String double map key must be string type and value must be number type");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((attributeDocument.HasMember(AWSGameLiftMatchmakingPlayerAttributeSLTypeName) ||
|
||||
attributeDocument.HasMember(AWSGameLiftMatchmakingPlayerAttributeSLServerTypeName)) &&
|
||||
attributeDocument.MemberBegin()->value.IsArray())
|
||||
{
|
||||
playerAttribute = Aws::GameLift::Server::Model::AttributeValue::ConstructStringList();
|
||||
for (auto iter = attributeDocument.MemberBegin()->value.Begin();
|
||||
iter != attributeDocument.MemberBegin()->value.End(); iter++)
|
||||
{
|
||||
if (iter->IsString())
|
||||
{
|
||||
playerAttribute.AddString(iter->GetString());
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftMatchmakingPlayerAttributeInvalidErrorMessage,
|
||||
player.m_playerId.c_str(), "String list element must be string type");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftMatchmakingPlayerAttributeInvalidErrorMessage,
|
||||
player.m_playerId.c_str(), "S, N, SDM or SLM is expected as attribute type.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftMatchmakingPlayerAttributeInvalidErrorMessage,
|
||||
player.m_playerId.c_str(), rapidjson::GetParseError_En(parseResult.Code()));
|
||||
return false;
|
||||
}
|
||||
outBackfillPlayer.AddPlayerAttribute(attributePair.first.c_str(), playerAttribute);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
AZStd::vector<AWSGameLiftPlayer> AWSGameLiftServerManager::GetActiveServerMatchBackfillPlayers()
|
||||
{
|
||||
AZStd::vector<AWSGameLiftPlayer> activePlayers;
|
||||
// Keep processing only when game session has matchmaking data
|
||||
if (IsMatchmakingDataValid())
|
||||
{
|
||||
auto activePlayerSessions = GetActivePlayerSessions();
|
||||
for (auto playerSession : activePlayerSessions)
|
||||
{
|
||||
AWSGameLiftPlayer player;
|
||||
if (BuildActiveServerMatchBackfillPlayer(playerSession.GetPlayerId().c_str(), player))
|
||||
{
|
||||
activePlayers.push_back(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
return activePlayers;
|
||||
}
|
||||
|
||||
bool AWSGameLiftServerManager::IsMatchmakingDataValid()
|
||||
{
|
||||
return m_matchmakingData.IsObject() &&
|
||||
m_matchmakingData.HasMember(AWSGameLiftMatchmakingConfigurationKeyName) &&
|
||||
m_matchmakingData.HasMember(AWSGameLiftMatchmakingTeamsKeyName);
|
||||
}
|
||||
|
||||
AZStd::vector<Aws::GameLift::Server::Model::PlayerSession> AWSGameLiftServerManager::GetActivePlayerSessions()
|
||||
{
|
||||
Aws::GameLift::Server::Model::DescribePlayerSessionsRequest describeRequest;
|
||||
describeRequest.SetGameSessionId(m_gameSession.GetGameSessionId());
|
||||
describeRequest.SetPlayerSessionStatusFilter(
|
||||
Aws::GameLift::Server::Model::PlayerSessionStatusMapper::GetNameForPlayerSessionStatus(
|
||||
Aws::GameLift::Server::Model::PlayerSessionStatus::ACTIVE));
|
||||
int maxPlayerSession = m_gameSession.GetMaximumPlayerSessionCount();
|
||||
|
||||
AZStd::vector<Aws::GameLift::Server::Model::PlayerSession> activePlayerSessions;
|
||||
if (maxPlayerSession <= AWSGameLiftDescribePlayerSessionsPageSize)
|
||||
{
|
||||
describeRequest.SetLimit(maxPlayerSession);
|
||||
auto outcome = m_gameLiftServerSDKWrapper->DescribePlayerSessions(describeRequest);
|
||||
if (outcome.IsSuccess())
|
||||
{
|
||||
for (auto playerSession : outcome.GetResult().GetPlayerSessions())
|
||||
{
|
||||
activePlayerSessions.push_back(playerSession);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftDescribePlayerSessionsErrorMessage,
|
||||
outcome.GetError().GetErrorMessage().c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
describeRequest.SetLimit(AWSGameLiftDescribePlayerSessionsPageSize);
|
||||
while (true)
|
||||
{
|
||||
auto outcome = m_gameLiftServerSDKWrapper->DescribePlayerSessions(describeRequest);
|
||||
if (outcome.IsSuccess())
|
||||
{
|
||||
for (auto playerSession : outcome.GetResult().GetPlayerSessions())
|
||||
{
|
||||
activePlayerSessions.push_back(playerSession);
|
||||
}
|
||||
if (outcome.GetResult().GetNextToken().empty())
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
describeRequest.SetNextToken(outcome.GetResult().GetNextToken());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
activePlayerSessions.clear();
|
||||
AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftDescribePlayerSessionsErrorMessage,
|
||||
outcome.GetError().GetErrorMessage().c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return activePlayerSessions;
|
||||
}
|
||||
|
||||
bool AWSGameLiftServerManager::BuildActiveServerMatchBackfillPlayer(const AZStd::string& playerId, AWSGameLiftPlayer& outPlayer)
|
||||
{
|
||||
// As data is from GameLift service, assume it is always in correct format
|
||||
rapidjson::Value& teams = m_matchmakingData[AWSGameLiftMatchmakingTeamsKeyName];
|
||||
|
||||
// Iterate through teams to find target player
|
||||
for (rapidjson::SizeType teamIndex = 0; teamIndex < teams.Size(); ++teamIndex)
|
||||
{
|
||||
rapidjson::Value& players = teams[teamIndex][AWSGameLiftMatchmakingPlayersKeyName];
|
||||
|
||||
// Iterate through players under the team to find target player
|
||||
for (rapidjson::SizeType playerIndex = 0; playerIndex < players.Size(); ++playerIndex)
|
||||
{
|
||||
if (std::strcmp(players[playerIndex][AWSGameLiftMatchmakingPlayerIdKeyName].GetString(), playerId.c_str()) == 0)
|
||||
{
|
||||
outPlayer.m_playerId = playerId;
|
||||
outPlayer.m_team = teams[teamIndex][AWSGameLiftMatchmakingTeamNameKeyName].GetString();
|
||||
// Get player attributes if target player has
|
||||
if (players[playerIndex].HasMember(AWSGameLiftMatchmakingPlayerAttributesKeyName))
|
||||
{
|
||||
BuildServerMatchBackfillPlayerAttributes(
|
||||
players[playerIndex][AWSGameLiftMatchmakingPlayerAttributesKeyName], outPlayer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void AWSGameLiftServerManager::BuildServerMatchBackfillPlayerAttributes(
|
||||
const rapidjson::Value& playerAttributes, AWSGameLiftPlayer& outPlayer)
|
||||
{
|
||||
for (auto iter = playerAttributes.MemberBegin(); iter != playerAttributes.MemberEnd(); iter++)
|
||||
{
|
||||
AZStd::string attributeName = iter->name.GetString();
|
||||
|
||||
rapidjson::StringBuffer jsonStringBuffer;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(jsonStringBuffer);
|
||||
iter->value[AWSGameLiftMatchmakingPlayerAttributeValueKeyName].Accept(writer);
|
||||
AZStd::string attributeType = iter->value[AWSGameLiftMatchmakingPlayerAttributeTypeKeyName].GetString();
|
||||
AZStd::string attributeValue = AZStd::string::format("{\"%s\": %s}",
|
||||
attributeType.c_str(), jsonStringBuffer.GetString());
|
||||
|
||||
outPlayer.m_playerAttributes.emplace(attributeName, attributeValue);
|
||||
}
|
||||
}
|
||||
|
||||
bool AWSGameLiftServerManager::BuildStartMatchBackfillRequest(
|
||||
const AZStd::string& ticketId,
|
||||
const AZStd::vector<AWSGameLiftPlayer>& players,
|
||||
Aws::GameLift::Server::Model::StartMatchBackfillRequest& outRequest)
|
||||
{
|
||||
outRequest.SetGameSessionArn(m_gameSession.GetGameSessionId());
|
||||
outRequest.SetMatchmakingConfigurationArn(m_matchmakingData[AWSGameLiftMatchmakingConfigurationKeyName].GetString());
|
||||
if (!ticketId.empty())
|
||||
{
|
||||
outRequest.SetTicketId(ticketId.c_str());
|
||||
}
|
||||
|
||||
AZStd::vector<AWSGameLiftPlayer> requestPlayers(players);
|
||||
if (players.size() == 0)
|
||||
{
|
||||
requestPlayers = GetActiveServerMatchBackfillPlayers();
|
||||
}
|
||||
for (auto player : requestPlayers)
|
||||
{
|
||||
Aws::GameLift::Server::Model::Player backfillPlayer;
|
||||
if (BuildServerMatchBackfillPlayer(player, backfillPlayer))
|
||||
{
|
||||
outRequest.AddPlayer(backfillPlayer);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void AWSGameLiftServerManager::BuildStopMatchBackfillRequest(
|
||||
const AZStd::string& ticketId, Aws::GameLift::Server::Model::StopMatchBackfillRequest& outRequest)
|
||||
{
|
||||
outRequest.SetGameSessionArn(m_gameSession.GetGameSessionId());
|
||||
outRequest.SetMatchmakingConfigurationArn(m_matchmakingData[AWSGameLiftMatchmakingConfigurationKeyName].GetString());
|
||||
if (!ticketId.empty())
|
||||
{
|
||||
outRequest.SetTicketId(ticketId.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
AZ::IO::Path AWSGameLiftServerManager::GetExternalSessionCertificate()
|
||||
{
|
||||
// TODO: Add support to get TLS cert file path
|
||||
@@ -238,7 +512,7 @@ namespace AWSGameLift
|
||||
|
||||
Aws::GameLift::Server::ProcessParameters processReadyParameter = Aws::GameLift::Server::ProcessParameters(
|
||||
AZStd::bind(&AWSGameLiftServerManager::OnStartGameSession, this, AZStd::placeholders::_1),
|
||||
AZStd::bind(&AWSGameLiftServerManager::OnUpdateGameSession, this),
|
||||
AZStd::bind(&AWSGameLiftServerManager::OnUpdateGameSession, this, AZStd::placeholders::_1),
|
||||
AZStd::bind(&AWSGameLiftServerManager::OnProcessTerminate, this),
|
||||
AZStd::bind(&AWSGameLiftServerManager::OnHealthCheck, this), desc.m_port,
|
||||
Aws::GameLift::Server::LogParameters(logPaths));
|
||||
@@ -260,6 +534,7 @@ namespace AWSGameLift
|
||||
|
||||
void AWSGameLiftServerManager::OnStartGameSession(const Aws::GameLift::Server::Model::GameSession& gameSession)
|
||||
{
|
||||
UpdateGameSessionData(gameSession);
|
||||
AzFramework::SessionConfig sessionConfig = BuildSessionConfig(gameSession);
|
||||
|
||||
bool createSessionResult = true;
|
||||
@@ -311,10 +586,19 @@ namespace AWSGameLift
|
||||
return m_serverSDKInitialized && healthCheckResult;
|
||||
}
|
||||
|
||||
void AWSGameLiftServerManager::OnUpdateGameSession()
|
||||
void AWSGameLiftServerManager::OnUpdateGameSession(const Aws::GameLift::Server::Model::UpdateGameSession& updateGameSession)
|
||||
{
|
||||
// TODO: Perform game-specific tasks to prep for newly matched players
|
||||
return;
|
||||
Aws::GameLift::Server::Model::UpdateReason updateReason = updateGameSession.GetUpdateReason();
|
||||
if (updateReason == Aws::GameLift::Server::Model::UpdateReason::MATCHMAKING_DATA_UPDATED)
|
||||
{
|
||||
UpdateGameSessionData(updateGameSession.GetGameSession());
|
||||
}
|
||||
AzFramework::SessionConfig sessionConfig = BuildSessionConfig(updateGameSession.GetGameSession());
|
||||
|
||||
AzFramework::SessionNotificationBus::Broadcast(
|
||||
&AzFramework::SessionNotifications::OnUpdateSessionBegin,
|
||||
sessionConfig,
|
||||
Aws::GameLift::Server::Model::UpdateReasonMapper::GetNameForUpdateReason(updateReason).c_str());
|
||||
}
|
||||
|
||||
bool AWSGameLiftServerManager::RemoveConnectedPlayer(uint32_t playerConnectionId, AZStd::string& outPlayerSessionId)
|
||||
@@ -340,6 +624,92 @@ namespace AWSGameLift
|
||||
m_gameLiftServerSDKWrapper = AZStd::move(gameLiftServerSDKWrapper);
|
||||
}
|
||||
|
||||
bool AWSGameLiftServerManager::StartMatchBackfill(const AZStd::string& ticketId, const AZStd::vector<AWSGameLiftPlayer>& players)
|
||||
{
|
||||
if (!m_serverSDKInitialized)
|
||||
{
|
||||
AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftServerSDKNotInitErrorMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsMatchmakingDataValid())
|
||||
{
|
||||
AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftMatchmakingDataMissingErrorMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
Aws::GameLift::Server::Model::StartMatchBackfillRequest request;
|
||||
if (!BuildStartMatchBackfillRequest(ticketId, players, request))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ_TracePrintf(AWSGameLiftServerManagerName, "Starting match backfill %s ...", ticketId.c_str());
|
||||
auto outcome = m_gameLiftServerSDKWrapper->StartMatchBackfill(request);
|
||||
if (!outcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftStartMatchBackfillErrorMessage,
|
||||
outcome.GetError().GetErrorMessage().c_str());
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_TracePrintf(AWSGameLiftServerManagerName, "StartMatchBackfill request against Amazon GameLift service is complete.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool AWSGameLiftServerManager::StopMatchBackfill(const AZStd::string& ticketId)
|
||||
{
|
||||
if (!m_serverSDKInitialized)
|
||||
{
|
||||
AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftServerSDKNotInitErrorMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsMatchmakingDataValid())
|
||||
{
|
||||
AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftMatchmakingDataMissingErrorMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
Aws::GameLift::Server::Model::StopMatchBackfillRequest request;
|
||||
BuildStopMatchBackfillRequest(ticketId, request);
|
||||
|
||||
AZ_TracePrintf(AWSGameLiftServerManagerName, "Stopping match backfill %s ...", ticketId.c_str());
|
||||
auto outcome = m_gameLiftServerSDKWrapper->StopMatchBackfill(request);
|
||||
if (!outcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftStopMatchBackfillErrorMessage,
|
||||
outcome.GetError().GetErrorMessage().c_str());
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_TracePrintf(AWSGameLiftServerManagerName, "StopMatchBackfill request against Amazon GameLift service is complete.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void AWSGameLiftServerManager::UpdateGameSessionData(const Aws::GameLift::Server::Model::GameSession& gameSession)
|
||||
{
|
||||
AZ_TracePrintf(AWSGameLiftServerManagerName, "Lazy loading game session and matchmaking data from Amazon GameLift service ...");
|
||||
m_gameSession = Aws::GameLift::Server::Model::GameSession(gameSession);
|
||||
if (m_gameSession.GetMatchmakerData().empty())
|
||||
{
|
||||
m_matchmakingData.Parse("{}");
|
||||
}
|
||||
else
|
||||
{
|
||||
rapidjson::ParseResult parseResult = m_matchmakingData.Parse(m_gameSession.GetMatchmakerData().c_str());
|
||||
if (!parseResult)
|
||||
{
|
||||
AZ_Error(AWSGameLiftServerManagerName, false,
|
||||
AWSGameLiftMatchmakingDataInvalidErrorMessage, rapidjson::GetParseError_En(parseResult.Code()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AWSGameLiftServerManager::ValidatePlayerJoinSession(const AzFramework::PlayerConnectionConfig& playerConnectionConfig)
|
||||
{
|
||||
uint32_t playerConnectionId = playerConnectionConfig.m_playerConnectionId;
|
||||
|
||||
@@ -11,11 +11,15 @@
|
||||
#include <aws/gamelift/server/GameLiftServerAPI.h>
|
||||
#include <aws/gamelift/server/model/GameSession.h>
|
||||
|
||||
#include <AzCore/JSON/rapidjson.h>
|
||||
#include <AzCore/JSON/document.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <AzFramework/Session/ISessionHandlingRequests.h>
|
||||
#include <AzFramework/Session/SessionConfig.h>
|
||||
|
||||
#include <AWSGameLiftPlayer.h>
|
||||
#include <Request/IAWSGameLiftServerRequests.h>
|
||||
|
||||
namespace AWSGameLift
|
||||
@@ -66,6 +70,36 @@ namespace AWSGameLift
|
||||
"Invalid player connection config, player connection id: %d, player session id: %s";
|
||||
static constexpr const char AWSGameLiftServerRemovePlayerSessionErrorMessage[] =
|
||||
"Failed to notify GameLift that the player with the player session id %s has disconnected from the server process. ErrorMessage: %s";
|
||||
static constexpr const char AWSGameLiftMatchmakingDataInvalidErrorMessage[] =
|
||||
"Failed to parse GameLift matchmaking data. ErrorMessage: %s";
|
||||
static constexpr const char AWSGameLiftMatchmakingDataMissingErrorMessage[] =
|
||||
"GameLift matchmaking data is missing or invalid to parse.";
|
||||
static constexpr const char AWSGameLiftMatchmakingPlayerAttributeInvalidErrorMessage[] =
|
||||
"Failed to build player %s attributes. ErrorMessage: %s";
|
||||
static constexpr const char AWSGameLiftDescribePlayerSessionsErrorMessage[] =
|
||||
"Failed to describe player sessions. ErrorMessage: %s";
|
||||
static constexpr const char AWSGameLiftStartMatchBackfillErrorMessage[] =
|
||||
"Failed to start match backfill. ErrorMessage: %s";
|
||||
static constexpr const char AWSGameLiftStopMatchBackfillErrorMessage[] =
|
||||
"Failed to stop match backfill. ErrorMessage: %s";
|
||||
|
||||
static constexpr const char AWSGameLiftMatchmakingConfigurationKeyName[] = "matchmakingConfigurationArn";
|
||||
static constexpr const char AWSGameLiftMatchmakingTeamsKeyName[] = "teams";
|
||||
static constexpr const char AWSGameLiftMatchmakingTeamNameKeyName[] = "name";
|
||||
static constexpr const char AWSGameLiftMatchmakingPlayersKeyName[] = "players";
|
||||
static constexpr const char AWSGameLiftMatchmakingPlayerIdKeyName[] = "playerId";
|
||||
static constexpr const char AWSGameLiftMatchmakingPlayerAttributesKeyName[] = "attributes";
|
||||
static constexpr const char AWSGameLiftMatchmakingPlayerAttributeTypeKeyName[] = "attributeType";
|
||||
static constexpr const char AWSGameLiftMatchmakingPlayerAttributeValueKeyName[] = "valueAttribute";
|
||||
static constexpr const char AWSGameLiftMatchmakingPlayerAttributeSTypeName[] = "S";
|
||||
static constexpr const char AWSGameLiftMatchmakingPlayerAttributeSServerTypeName[] = "STRING";
|
||||
static constexpr const char AWSGameLiftMatchmakingPlayerAttributeNTypeName[] = "N";
|
||||
static constexpr const char AWSGameLiftMatchmakingPlayerAttributeNServerTypeName[] = "NUMBER";
|
||||
static constexpr const char AWSGameLiftMatchmakingPlayerAttributeSLTypeName[] = "SL";
|
||||
static constexpr const char AWSGameLiftMatchmakingPlayerAttributeSLServerTypeName[] = "STRING_LIST";
|
||||
static constexpr const char AWSGameLiftMatchmakingPlayerAttributeSDMTypeName[] = "SDM";
|
||||
static constexpr const char AWSGameLiftMatchmakingPlayerAttributeSDMServerTypeName[] = "STRING_DOUBLE_MAP";
|
||||
static constexpr const uint16_t AWSGameLiftDescribePlayerSessionsPageSize = 30;
|
||||
|
||||
AWSGameLiftServerManager();
|
||||
virtual ~AWSGameLiftServerManager();
|
||||
@@ -78,6 +112,8 @@ namespace AWSGameLift
|
||||
|
||||
// AWSGameLiftServerRequestBus interface implementation
|
||||
bool NotifyGameLiftProcessReady() override;
|
||||
bool StartMatchBackfill(const AZStd::string& ticketId, const AZStd::vector<AWSGameLiftPlayer>& players) override;
|
||||
bool StopMatchBackfill(const AZStd::string& ticketId) override;
|
||||
|
||||
// ISessionHandlingProviderRequests interface implementation
|
||||
void HandleDestroySession() override;
|
||||
@@ -92,18 +128,48 @@ namespace AWSGameLift
|
||||
//! Add connected player session id.
|
||||
bool AddConnectedPlayer(const AzFramework::PlayerConnectionConfig& playerConnectionConfig);
|
||||
|
||||
//! Get active server player data from lazy loaded game session for server match backfill
|
||||
AZStd::vector<AWSGameLiftPlayer> GetActiveServerMatchBackfillPlayers();
|
||||
|
||||
//! Update local game session data to latest one
|
||||
void UpdateGameSessionData(const Aws::GameLift::Server::Model::GameSession& gameSession);
|
||||
|
||||
private:
|
||||
//! Build the serverProcessDesc with appropriate server port number and log paths.
|
||||
GameLiftServerProcessDesc BuildGameLiftServerProcessDesc();
|
||||
|
||||
//! Build active server player data from lazy loaded game session based on player id
|
||||
bool BuildActiveServerMatchBackfillPlayer(const AZStd::string& playerId, AWSGameLiftPlayer& outPlayer);
|
||||
|
||||
//! Build server player attribute data from lazy load matchmaking data
|
||||
void BuildServerMatchBackfillPlayerAttributes(const rapidjson::Value& playerAttributes, AWSGameLiftPlayer& outPlayer);
|
||||
|
||||
//! Build server player data for server match backfill
|
||||
bool BuildServerMatchBackfillPlayer(const AWSGameLiftPlayer& player, Aws::GameLift::Server::Model::Player& outBackfillPlayer);
|
||||
|
||||
//! Build start match backfill request for StartMatchBackfill operation
|
||||
bool BuildStartMatchBackfillRequest(
|
||||
const AZStd::string& ticketId,
|
||||
const AZStd::vector<AWSGameLiftPlayer>& players,
|
||||
Aws::GameLift::Server::Model::StartMatchBackfillRequest& outRequest);
|
||||
|
||||
//! Build stop match backfill request for StopMatchBackfill operation
|
||||
void BuildStopMatchBackfillRequest(const AZStd::string& ticketId, Aws::GameLift::Server::Model::StopMatchBackfillRequest& outRequest);
|
||||
|
||||
//! Build session config by using AWS GameLift Server GameSession Model.
|
||||
AzFramework::SessionConfig BuildSessionConfig(const Aws::GameLift::Server::Model::GameSession& gameSession);
|
||||
|
||||
//! Check whether matchmaking data is in proper format
|
||||
bool IsMatchmakingDataValid();
|
||||
|
||||
//! Fetch active player sessions in game session.
|
||||
AZStd::vector<Aws::GameLift::Server::Model::PlayerSession> GetActivePlayerSessions();
|
||||
|
||||
//! Callback function that the GameLift service invokes to activate a new game session.
|
||||
void OnStartGameSession(const Aws::GameLift::Server::Model::GameSession& gameSession);
|
||||
|
||||
//! Callback function that the GameLift service invokes to pass an updated game session object to the server process.
|
||||
void OnUpdateGameSession();
|
||||
void OnUpdateGameSession(const Aws::GameLift::Server::Model::UpdateGameSession& updateGameSession);
|
||||
|
||||
//! Callback function that the server process or GameLift service invokes to force the server process to shut down.
|
||||
void OnProcessTerminate();
|
||||
@@ -125,5 +191,12 @@ namespace AWSGameLift
|
||||
using PlayerConnectionId = uint32_t;
|
||||
using PlayerSessionId = AZStd::string;
|
||||
AZStd::unordered_map<PlayerConnectionId, PlayerSessionId> m_connectedPlayers;
|
||||
|
||||
// Lazy loaded game session and matchmaking data
|
||||
Aws::GameLift::Server::Model::GameSession m_gameSession;
|
||||
// Matchmaking data contains a unique match ID, it identifies the matchmaker that created the match
|
||||
// and describes the teams, team assignments, and players.
|
||||
// Reference https://docs.aws.amazon.com/gamelift/latest/flexmatchguide/match-server.html#match-server-data
|
||||
rapidjson::Document m_matchmakingData;
|
||||
};
|
||||
} // namespace AWSGameLift
|
||||
|
||||
@@ -22,6 +22,12 @@ namespace AWSGameLift
|
||||
return Aws::GameLift::Server::ActivateGameSession();
|
||||
}
|
||||
|
||||
Aws::GameLift::DescribePlayerSessionsOutcome GameLiftServerSDKWrapper::DescribePlayerSessions(
|
||||
const Aws::GameLift::Server::Model::DescribePlayerSessionsRequest& describePlayerSessionsRequest)
|
||||
{
|
||||
return Aws::GameLift::Server::DescribePlayerSessions(describePlayerSessionsRequest);
|
||||
}
|
||||
|
||||
Aws::GameLift::Server::InitSDKOutcome GameLiftServerSDKWrapper::InitSDK()
|
||||
{
|
||||
return Aws::GameLift::Server::InitSDK();
|
||||
@@ -69,4 +75,17 @@ namespace AWSGameLift
|
||||
{
|
||||
return Aws::GameLift::Server::RemovePlayerSession(playerSessionId.c_str());
|
||||
}
|
||||
|
||||
Aws::GameLift::StartMatchBackfillOutcome GameLiftServerSDKWrapper::StartMatchBackfill(
|
||||
const Aws::GameLift::Server::Model::StartMatchBackfillRequest& startMatchBackfillRequest)
|
||||
{
|
||||
return Aws::GameLift::Server::StartMatchBackfill(startMatchBackfillRequest);
|
||||
}
|
||||
|
||||
Aws::GameLift::GenericOutcome GameLiftServerSDKWrapper::StopMatchBackfill(
|
||||
const Aws::GameLift::Server::Model::StopMatchBackfillRequest& stopMatchBackfillRequest)
|
||||
{
|
||||
return Aws::GameLift::Server::StopMatchBackfill(stopMatchBackfillRequest);
|
||||
}
|
||||
|
||||
} // namespace AWSGameLift
|
||||
|
||||
@@ -33,6 +33,14 @@ namespace AWSGameLift
|
||||
//! @return Returns a generic outcome consisting of success or failure with an error message.
|
||||
virtual Aws::GameLift::GenericOutcome ActivateGameSession();
|
||||
|
||||
//! Retrieves player session data, including settings, session metadata, and player data.
|
||||
//! Use this action to get information for a single player session,
|
||||
//! for all player sessions in a game session, or for all player sessions associated with a single player ID.
|
||||
//! @param describePlayerSessionsRequest The request object describing which player sessions to retrieve.
|
||||
//! @return If successful, returns a DescribePlayerSessionsOutcome object containing a set of player session objects that fit the request parameters.
|
||||
virtual Aws::GameLift::DescribePlayerSessionsOutcome DescribePlayerSessions(
|
||||
const Aws::GameLift::Server::Model::DescribePlayerSessionsRequest& describePlayerSessionsRequest);
|
||||
|
||||
//! Initializes the GameLift SDK.
|
||||
//! Should be called when the server starts, before any GameLift-dependent initialization happens.
|
||||
//! @return If successful, returns an InitSdkOutcome object indicating that the server process is ready to call ProcessReady().
|
||||
@@ -56,5 +64,16 @@ namespace AWSGameLift
|
||||
//! @param playerSessionId Unique ID issued by the Amazon GameLift service in response to a call to the AWS SDK Amazon GameLift API action CreatePlayerSession.
|
||||
//! @return Returns a generic outcome consisting of success or failure with an error message.
|
||||
virtual Aws::GameLift::GenericOutcome RemovePlayerSession(const AZStd::string& playerSessionId);
|
||||
|
||||
//! Sends a request to find new players for open slots in a game session created with FlexMatch.
|
||||
//! When the match has been successfully, backfilled updated matchmaker data will be sent to the OnUpdateGameSession callback.
|
||||
//! @param startMatchBackfillRequest This data type is used to send a matchmaking backfill request.
|
||||
//! @return Returns a StartMatchBackfillOutcome object with the match backfill ticket or failure with an error message.
|
||||
virtual Aws::GameLift::StartMatchBackfillOutcome StartMatchBackfill(const Aws::GameLift::Server::Model::StartMatchBackfillRequest& startMatchBackfillRequest);
|
||||
|
||||
//! Cancels an active match backfill request that was created with StartMatchBackfill
|
||||
//! @param stopMatchBackfillRequest This data type is used to cancel a matchmaking backfill request.
|
||||
//! @return Returns a generic outcome consisting of success or failure with an error message.
|
||||
virtual Aws::GameLift::GenericOutcome StopMatchBackfill(const Aws::GameLift::Server::Model::StopMatchBackfillRequest& stopMatchBackfillRequest);
|
||||
};
|
||||
} // namespace AWSGameLift
|
||||
|
||||
@@ -16,6 +16,136 @@
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
static constexpr const char TEST_SERVER_MATCHMAKING_DATA[] =
|
||||
R"({
|
||||
"matchId":"testmatchid",
|
||||
"matchmakingConfigurationArn":"testmatchconfig",
|
||||
"teams":[
|
||||
{"name":"testteam",
|
||||
"players":[
|
||||
{"playerId":"testplayer",
|
||||
"attributes":{
|
||||
"skills":{
|
||||
"attributeType":"STRING_DOUBLE_MAP",
|
||||
"valueAttribute":{"test1":10.0,"test2":20.0,"test3":30.0,"test4":40.0}
|
||||
},
|
||||
"mode":{
|
||||
"attributeType":"STRING",
|
||||
"valueAttribute":"testmode"
|
||||
},
|
||||
"level":{
|
||||
"attributeType":"NUMBER",
|
||||
"valueAttribute":10.0
|
||||
},
|
||||
"items":{
|
||||
"attributeType":"STRING_LIST",
|
||||
"valueAttribute":["test1","test2","test3"]
|
||||
}
|
||||
}}
|
||||
]}
|
||||
]
|
||||
})";
|
||||
|
||||
Aws::GameLift::Server::Model::StartMatchBackfillRequest GetTestStartMatchBackfillRequest()
|
||||
{
|
||||
Aws::GameLift::Server::Model::StartMatchBackfillRequest request;
|
||||
request.SetMatchmakingConfigurationArn("testmatchconfig");
|
||||
Aws::GameLift::Server::Model::Player player;
|
||||
player.SetPlayerId("testplayer");
|
||||
player.SetTeam("testteam");
|
||||
player.AddPlayerAttribute("mode", Aws::GameLift::Server::Model::AttributeValue("testmode"));
|
||||
player.AddPlayerAttribute("level", Aws::GameLift::Server::Model::AttributeValue(10.0));
|
||||
auto sdmValue = Aws::GameLift::Server::Model::AttributeValue::ConstructStringDoubleMap();
|
||||
sdmValue.AddStringAndDouble("test1", 10.0);
|
||||
player.AddPlayerAttribute("skills", sdmValue);
|
||||
auto slValue = Aws::GameLift::Server::Model::AttributeValue::ConstructStringList();
|
||||
slValue.AddString("test1");
|
||||
player.AddPlayerAttribute("items", slValue);
|
||||
player.AddLatencyInMs("testregion", 10);
|
||||
request.AddPlayer(player);
|
||||
request.SetTicketId("testticket");
|
||||
return request;
|
||||
}
|
||||
|
||||
AWSGameLiftPlayer GetTestGameLiftPlayer()
|
||||
{
|
||||
AWSGameLiftPlayer player;
|
||||
player.m_team = "testteam";
|
||||
player.m_playerId = "testplayer";
|
||||
player.m_playerAttributes.emplace("mode", "{\"S\": \"testmode\"}");
|
||||
player.m_playerAttributes.emplace("level", "{\"N\": 10.0}");
|
||||
player.m_playerAttributes.emplace("skills", "{\"SDM\": {\"test1\":10.0}}");
|
||||
player.m_playerAttributes.emplace("items", "{\"SL\": [\"test1\"]}");
|
||||
player.m_latencyInMs.emplace("testregion", 10);
|
||||
return player;
|
||||
}
|
||||
|
||||
MATCHER_P(StartMatchBackfillRequestMatcher, expectedRequest, "")
|
||||
{
|
||||
// Custome matcher for checking the SearchSessionsResponse type argument.
|
||||
AZ_UNUSED(result_listener);
|
||||
if (strcmp(arg.GetGameSessionArn().c_str(), expectedRequest.GetGameSessionArn().c_str()) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (strcmp(arg.GetMatchmakingConfigurationArn().c_str(), expectedRequest.GetMatchmakingConfigurationArn().c_str()) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (strcmp(arg.GetTicketId().c_str(), expectedRequest.GetTicketId().c_str()) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (arg.GetPlayers().size() != expectedRequest.GetPlayers().size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (int playerIndex = 0; playerIndex < expectedRequest.GetPlayers().size(); playerIndex++)
|
||||
{
|
||||
auto actualPlayerAttributes = arg.GetPlayers()[playerIndex].GetPlayerAttributes();
|
||||
auto expectedPlayerAttributes = expectedRequest.GetPlayers()[playerIndex].GetPlayerAttributes();
|
||||
if (actualPlayerAttributes.size() != expectedPlayerAttributes.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (auto attributePair : expectedPlayerAttributes)
|
||||
{
|
||||
if (actualPlayerAttributes.find(attributePair.first) == actualPlayerAttributes.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!(attributePair.second.GetType() == actualPlayerAttributes[attributePair.first].GetType() &&
|
||||
(attributePair.second.GetS() == actualPlayerAttributes[attributePair.first].GetS() ||
|
||||
attributePair.second.GetN() == actualPlayerAttributes[attributePair.first].GetN() ||
|
||||
attributePair.second.GetSL() == actualPlayerAttributes[attributePair.first].GetSL() ||
|
||||
attributePair.second.GetSDM() == actualPlayerAttributes[attributePair.first].GetSDM())))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto actualLatencies = arg.GetPlayers()[playerIndex].GetLatencyInMs();
|
||||
auto expectedLatencies = expectedRequest.GetPlayers()[playerIndex].GetLatencyInMs();
|
||||
if (actualLatencies.size() != expectedLatencies.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (auto latencyPair : expectedLatencies)
|
||||
{
|
||||
if (actualLatencies.find(latencyPair.first) == actualLatencies.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (latencyPair.second != actualLatencies[latencyPair.first])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class SessionNotificationsHandlerMock
|
||||
: public AzFramework::SessionNotificationBus::Handler
|
||||
{
|
||||
@@ -33,6 +163,7 @@ namespace UnitTest
|
||||
MOCK_METHOD0(OnSessionHealthCheck, bool());
|
||||
MOCK_METHOD1(OnCreateSessionBegin, bool(const AzFramework::SessionConfig&));
|
||||
MOCK_METHOD0(OnDestroySessionBegin, bool());
|
||||
MOCK_METHOD2(OnUpdateSessionBegin, void(const AzFramework::SessionConfig&, const AZStd::string&));
|
||||
};
|
||||
|
||||
class GameLiftServerManagerTest
|
||||
@@ -228,6 +359,64 @@ namespace UnitTest
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
||||
}
|
||||
|
||||
TEST_F(GameLiftServerManagerTest, OnUpdateGameSession_TriggerWithUnknownReason_OnUpdateSessionBeginGetCalledOnce)
|
||||
{
|
||||
m_serverManager->InitializeGameLiftServerSDK();
|
||||
m_serverManager->NotifyGameLiftProcessReady();
|
||||
SessionNotificationsHandlerMock handlerMock;
|
||||
EXPECT_CALL(handlerMock, OnUpdateSessionBegin(testing::_, testing::_)).Times(1);
|
||||
|
||||
m_serverManager->m_gameLiftServerSDKWrapperMockPtr->m_onUpdateGameSessionFunc(
|
||||
Aws::GameLift::Server::Model::UpdateGameSession(
|
||||
Aws::GameLift::Server::Model::GameSession(),
|
||||
Aws::GameLift::Server::Model::UpdateReason::UNKNOWN,
|
||||
"testticket"));
|
||||
}
|
||||
|
||||
TEST_F(GameLiftServerManagerTest, OnUpdateGameSession_TriggerWithEmptyMatchmakingData_OnUpdateSessionBeginGetCalledOnce)
|
||||
{
|
||||
m_serverManager->InitializeGameLiftServerSDK();
|
||||
m_serverManager->NotifyGameLiftProcessReady();
|
||||
SessionNotificationsHandlerMock handlerMock;
|
||||
EXPECT_CALL(handlerMock, OnUpdateSessionBegin(testing::_, testing::_)).Times(1);
|
||||
|
||||
m_serverManager->m_gameLiftServerSDKWrapperMockPtr->m_onUpdateGameSessionFunc(
|
||||
Aws::GameLift::Server::Model::UpdateGameSession(
|
||||
Aws::GameLift::Server::Model::GameSession(),
|
||||
Aws::GameLift::Server::Model::UpdateReason::MATCHMAKING_DATA_UPDATED,
|
||||
"testticket"));
|
||||
}
|
||||
|
||||
TEST_F(GameLiftServerManagerTest, OnUpdateGameSession_TriggerWithValidMatchmakingData_OnUpdateSessionBeginGetCalledOnce)
|
||||
{
|
||||
m_serverManager->InitializeGameLiftServerSDK();
|
||||
m_serverManager->NotifyGameLiftProcessReady();
|
||||
SessionNotificationsHandlerMock handlerMock;
|
||||
EXPECT_CALL(handlerMock, OnUpdateSessionBegin(testing::_, testing::_)).Times(1);
|
||||
|
||||
Aws::GameLift::Server::Model::GameSession gameSession;
|
||||
gameSession.SetMatchmakerData(TEST_SERVER_MATCHMAKING_DATA);
|
||||
m_serverManager->m_gameLiftServerSDKWrapperMockPtr->m_onUpdateGameSessionFunc(
|
||||
Aws::GameLift::Server::Model::UpdateGameSession(
|
||||
gameSession, Aws::GameLift::Server::Model::UpdateReason::MATCHMAKING_DATA_UPDATED, "testticket"));
|
||||
}
|
||||
|
||||
TEST_F(GameLiftServerManagerTest, OnUpdateGameSession_TriggerWithInvalidMatchmakingData_OnUpdateSessionBeginGetCalledOnce)
|
||||
{
|
||||
m_serverManager->InitializeGameLiftServerSDK();
|
||||
m_serverManager->NotifyGameLiftProcessReady();
|
||||
SessionNotificationsHandlerMock handlerMock;
|
||||
EXPECT_CALL(handlerMock, OnUpdateSessionBegin(testing::_, testing::_)).Times(1);
|
||||
|
||||
Aws::GameLift::Server::Model::GameSession gameSession;
|
||||
gameSession.SetMatchmakerData("{invalid}");
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
m_serverManager->m_gameLiftServerSDKWrapperMockPtr->m_onUpdateGameSessionFunc(
|
||||
Aws::GameLift::Server::Model::UpdateGameSession(
|
||||
gameSession, Aws::GameLift::Server::Model::UpdateReason::MATCHMAKING_DATA_UPDATED, "testticket"));
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
||||
}
|
||||
|
||||
TEST_F(GameLiftServerManagerTest, ValidatePlayerJoinSession_CallWithInvalidConnectionConfig_GetFalseResultAndExpectedErrorLog)
|
||||
{
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
@@ -425,4 +614,331 @@ namespace UnitTest
|
||||
}
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(testThreadNumber - 1); // The player is only disconnected once.
|
||||
}
|
||||
|
||||
TEST_F(GameLiftServerManagerTest, UpdateGameSessionData_CallWithInvalidMatchmakingData_GetExpectedError)
|
||||
{
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
m_serverManager->SetupTestMatchmakingData("{invalid}");
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
||||
}
|
||||
|
||||
TEST_F(GameLiftServerManagerTest, GetActiveServerMatchBackfillPlayers_CallWithInvalidMatchmakingData_GetEmptyResult)
|
||||
{
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
m_serverManager->SetupTestMatchmakingData("{invalid}");
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
||||
|
||||
auto actualResult = m_serverManager->GetTestServerMatchBackfillPlayers();
|
||||
EXPECT_TRUE(actualResult.empty());
|
||||
}
|
||||
|
||||
TEST_F(GameLiftServerManagerTest, GetActiveServerMatchBackfillPlayers_CallWithEmptyMatchmakingData_GetEmptyResult)
|
||||
{
|
||||
m_serverManager->SetupTestMatchmakingData("");
|
||||
|
||||
auto actualResult = m_serverManager->GetTestServerMatchBackfillPlayers();
|
||||
EXPECT_TRUE(actualResult.empty());
|
||||
}
|
||||
|
||||
TEST_F(GameLiftServerManagerTest, GetActiveServerMatchBackfillPlayers_CallButDescribePlayerError_GetEmptyResult)
|
||||
{
|
||||
m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA);
|
||||
|
||||
Aws::GameLift::GameLiftError error;
|
||||
Aws::GameLift::DescribePlayerSessionsOutcome errorOutcome(error);
|
||||
EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), DescribePlayerSessions(testing::_))
|
||||
.Times(1)
|
||||
.WillOnce(Return(errorOutcome));
|
||||
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
auto actualResult = m_serverManager->GetTestServerMatchBackfillPlayers();
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
||||
EXPECT_TRUE(actualResult.empty());
|
||||
}
|
||||
|
||||
TEST_F(GameLiftServerManagerTest, GetActiveServerMatchBackfillPlayers_CallButNoActivePlayer_GetEmptyResult)
|
||||
{
|
||||
m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA);
|
||||
|
||||
Aws::GameLift::Server::Model::DescribePlayerSessionsResult result;
|
||||
Aws::GameLift::DescribePlayerSessionsOutcome successOutcome(result);
|
||||
EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), DescribePlayerSessions(testing::_))
|
||||
.Times(1)
|
||||
.WillOnce(Return(successOutcome));
|
||||
|
||||
auto actualResult = m_serverManager->GetTestServerMatchBackfillPlayers();
|
||||
EXPECT_TRUE(actualResult.empty());
|
||||
}
|
||||
|
||||
TEST_F(GameLiftServerManagerTest, GetActiveServerMatchBackfillPlayers_CallWithValidMatchmakingData_GetExpectedResult)
|
||||
{
|
||||
m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA);
|
||||
|
||||
Aws::GameLift::Server::Model::PlayerSession playerSession;
|
||||
playerSession.SetPlayerId("testplayer");
|
||||
Aws::GameLift::Server::Model::DescribePlayerSessionsResult result;
|
||||
result.AddPlayerSessions(playerSession);
|
||||
Aws::GameLift::DescribePlayerSessionsOutcome successOutcome(result);
|
||||
EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), DescribePlayerSessions(testing::_))
|
||||
.Times(1)
|
||||
.WillOnce(Return(successOutcome));
|
||||
|
||||
auto actualResult = m_serverManager->GetTestServerMatchBackfillPlayers();
|
||||
EXPECT_TRUE(actualResult.size() == 1);
|
||||
EXPECT_TRUE(actualResult[0].m_team == "testteam");
|
||||
EXPECT_TRUE(actualResult[0].m_playerId == "testplayer");
|
||||
EXPECT_TRUE(actualResult[0].m_playerAttributes.size() == 4);
|
||||
}
|
||||
|
||||
TEST_F(GameLiftServerManagerTest, GetActiveServerMatchBackfillPlayers_CallWithMultiDescribePlayerButError_GetEmptyResult)
|
||||
{
|
||||
m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA, 50);
|
||||
|
||||
Aws::GameLift::GameLiftError error;
|
||||
Aws::GameLift::DescribePlayerSessionsOutcome errorOutcome(error);
|
||||
EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), DescribePlayerSessions(testing::_))
|
||||
.Times(1)
|
||||
.WillOnce(Return(errorOutcome));
|
||||
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
auto actualResult = m_serverManager->GetTestServerMatchBackfillPlayers();
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
||||
EXPECT_TRUE(actualResult.empty());
|
||||
}
|
||||
|
||||
TEST_F(GameLiftServerManagerTest, GetActiveServerMatchBackfillPlayers_CallWithMultiDescribePlayer_GetExpectedResult)
|
||||
{
|
||||
m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA, 50);
|
||||
|
||||
Aws::GameLift::Server::Model::PlayerSession playerSession1;
|
||||
playerSession1.SetPlayerId("testplayer");
|
||||
Aws::GameLift::Server::Model::DescribePlayerSessionsResult result1;
|
||||
result1.AddPlayerSessions(playerSession1);
|
||||
result1.SetNextToken("testtoken");
|
||||
Aws::GameLift::DescribePlayerSessionsOutcome successOutcome1(result1);
|
||||
|
||||
Aws::GameLift::Server::Model::PlayerSession playerSession2;
|
||||
playerSession2.SetPlayerId("playernotinmatch");
|
||||
Aws::GameLift::Server::Model::DescribePlayerSessionsResult result2;
|
||||
result2.AddPlayerSessions(playerSession2);
|
||||
Aws::GameLift::DescribePlayerSessionsOutcome successOutcome2(result2);
|
||||
EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), DescribePlayerSessions(testing::_))
|
||||
.WillOnce(Return(successOutcome1))
|
||||
.WillOnce(Return(successOutcome2));
|
||||
|
||||
auto actualResult = m_serverManager->GetTestServerMatchBackfillPlayers();
|
||||
EXPECT_TRUE(actualResult.size() == 1);
|
||||
EXPECT_TRUE(actualResult[0].m_team == "testteam");
|
||||
EXPECT_TRUE(actualResult[0].m_playerId == "testplayer");
|
||||
EXPECT_TRUE(actualResult[0].m_playerAttributes.size() == 4);
|
||||
}
|
||||
|
||||
TEST_F(GameLiftServerManagerTest, StartMatchBackfill_SDKNotInitialized_GetExpectedError)
|
||||
{
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
auto actualResult = m_serverManager->StartMatchBackfill("testticket", {});
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
||||
EXPECT_FALSE(actualResult);
|
||||
}
|
||||
|
||||
TEST_F(GameLiftServerManagerTest, StartMatchBackfill_CallWithEmptyMatchmakingData_GetExpectedError)
|
||||
{
|
||||
m_serverManager->InitializeGameLiftServerSDK();
|
||||
m_serverManager->SetupTestMatchmakingData("");
|
||||
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
auto actualResult = m_serverManager->StartMatchBackfill("testticket", {});
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
||||
EXPECT_FALSE(actualResult);
|
||||
}
|
||||
|
||||
TEST_F(GameLiftServerManagerTest, StartMatchBackfill_CallWithInvalidPlayerAttribute_GetExpectedError)
|
||||
{
|
||||
m_serverManager->InitializeGameLiftServerSDK();
|
||||
m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA);
|
||||
|
||||
AWSGameLiftPlayer testPlayer = GetTestGameLiftPlayer();
|
||||
testPlayer.m_playerAttributes.clear();
|
||||
testPlayer.m_playerAttributes.emplace("invalidattribute", "{invalid}");
|
||||
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
auto actualResult = m_serverManager->StartMatchBackfill("testticket", { testPlayer });
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
||||
EXPECT_FALSE(actualResult);
|
||||
}
|
||||
|
||||
TEST_F(GameLiftServerManagerTest, StartMatchBackfill_CallWithWrongPlayerAttributeType_GetExpectedError)
|
||||
{
|
||||
m_serverManager->InitializeGameLiftServerSDK();
|
||||
m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA);
|
||||
|
||||
AWSGameLiftPlayer testPlayer = GetTestGameLiftPlayer();
|
||||
testPlayer.m_playerAttributes.clear();
|
||||
testPlayer.m_playerAttributes.emplace("invalidattribute", "{\"SDM\": [\"test1\"]}");
|
||||
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
auto actualResult = m_serverManager->StartMatchBackfill("testticket", { testPlayer });
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
||||
EXPECT_FALSE(actualResult);
|
||||
}
|
||||
|
||||
TEST_F(GameLiftServerManagerTest, StartMatchBackfill_CallWithUnexpectedPlayerAttributeType_GetExpectedError)
|
||||
{
|
||||
m_serverManager->InitializeGameLiftServerSDK();
|
||||
m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA);
|
||||
|
||||
AWSGameLiftPlayer testPlayer = GetTestGameLiftPlayer();
|
||||
testPlayer.m_playerAttributes.clear();
|
||||
testPlayer.m_playerAttributes.emplace("invalidattribute", "{\"UNEXPECTED\": [\"test1\"]}");
|
||||
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
auto actualResult = m_serverManager->StartMatchBackfill("testticket", { testPlayer });
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
||||
EXPECT_FALSE(actualResult);
|
||||
}
|
||||
|
||||
TEST_F(GameLiftServerManagerTest, StartMatchBackfill_CallWithWrongSLPlayerAttributeValue_GetExpectedError)
|
||||
{
|
||||
m_serverManager->InitializeGameLiftServerSDK();
|
||||
m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA);
|
||||
|
||||
AWSGameLiftPlayer testPlayer = GetTestGameLiftPlayer();
|
||||
testPlayer.m_playerAttributes.clear();
|
||||
testPlayer.m_playerAttributes.emplace("invalidattribute", "{\"SL\": [10.0]}");
|
||||
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
auto actualResult = m_serverManager->StartMatchBackfill("testticket", { testPlayer });
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
||||
EXPECT_FALSE(actualResult);
|
||||
}
|
||||
|
||||
TEST_F(GameLiftServerManagerTest, StartMatchBackfill_CallWithWrongSDMPlayerAttributeValue_GetExpectedError)
|
||||
{
|
||||
m_serverManager->InitializeGameLiftServerSDK();
|
||||
m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA);
|
||||
|
||||
AWSGameLiftPlayer testPlayer = GetTestGameLiftPlayer();
|
||||
testPlayer.m_playerAttributes.clear();
|
||||
testPlayer.m_playerAttributes.emplace("invalidattribute", "{\"SDM\": {10.0: \"test1\"}}");
|
||||
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
auto actualResult = m_serverManager->StartMatchBackfill("testticket", { testPlayer });
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
||||
EXPECT_FALSE(actualResult);
|
||||
}
|
||||
|
||||
TEST_F(GameLiftServerManagerTest, StartMatchBackfill_CallWithValidPlayersData_GetExpectedResult)
|
||||
{
|
||||
m_serverManager->InitializeGameLiftServerSDK();
|
||||
m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA);
|
||||
|
||||
Aws::GameLift::Server::Model::StartMatchBackfillResult backfillResult;
|
||||
Aws::GameLift::StartMatchBackfillOutcome backfillSuccessOutcome(backfillResult);
|
||||
Aws::GameLift::Server::Model::StartMatchBackfillRequest request = GetTestStartMatchBackfillRequest();
|
||||
|
||||
EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), StartMatchBackfill(StartMatchBackfillRequestMatcher(request)))
|
||||
.Times(1)
|
||||
.WillOnce(Return(backfillSuccessOutcome));
|
||||
|
||||
AWSGameLiftPlayer testPlayer = GetTestGameLiftPlayer();
|
||||
auto actualResult = m_serverManager->StartMatchBackfill("testticket", {testPlayer});
|
||||
EXPECT_TRUE(actualResult);
|
||||
}
|
||||
|
||||
TEST_F(GameLiftServerManagerTest, StartMatchBackfill_CallWithoutGivingPlayersData_GetExpectedResult)
|
||||
{
|
||||
m_serverManager->InitializeGameLiftServerSDK();
|
||||
m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA);
|
||||
|
||||
Aws::GameLift::Server::Model::PlayerSession playerSession;
|
||||
playerSession.SetPlayerId("testplayer");
|
||||
Aws::GameLift::Server::Model::DescribePlayerSessionsResult result;
|
||||
result.AddPlayerSessions(playerSession);
|
||||
Aws::GameLift::DescribePlayerSessionsOutcome successOutcome(result);
|
||||
EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), DescribePlayerSessions(testing::_))
|
||||
.Times(1)
|
||||
.WillOnce(Return(successOutcome));
|
||||
|
||||
Aws::GameLift::Server::Model::StartMatchBackfillResult backfillResult;
|
||||
Aws::GameLift::StartMatchBackfillOutcome backfillSuccessOutcome(backfillResult);
|
||||
EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), StartMatchBackfill(testing::_))
|
||||
.Times(1)
|
||||
.WillOnce(Return(backfillSuccessOutcome));
|
||||
|
||||
auto actualResult = m_serverManager->StartMatchBackfill("testticket", {});
|
||||
EXPECT_TRUE(actualResult);
|
||||
}
|
||||
|
||||
TEST_F(GameLiftServerManagerTest, StartMatchBackfill_CallButStartBackfillFail_GetExpectedError)
|
||||
{
|
||||
m_serverManager->InitializeGameLiftServerSDK();
|
||||
m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA);
|
||||
|
||||
Aws::GameLift::Server::Model::PlayerSession playerSession;
|
||||
playerSession.SetPlayerId("testplayer");
|
||||
Aws::GameLift::Server::Model::DescribePlayerSessionsResult result;
|
||||
result.AddPlayerSessions(playerSession);
|
||||
Aws::GameLift::DescribePlayerSessionsOutcome successOutcome(result);
|
||||
EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), DescribePlayerSessions(testing::_))
|
||||
.Times(1)
|
||||
.WillOnce(Return(successOutcome));
|
||||
|
||||
Aws::GameLift::GameLiftError error;
|
||||
Aws::GameLift::StartMatchBackfillOutcome errorOutcome(error);
|
||||
EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), StartMatchBackfill(testing::_))
|
||||
.Times(1)
|
||||
.WillOnce(Return(errorOutcome));
|
||||
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
auto actualResult = m_serverManager->StartMatchBackfill("testticket", {});
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
||||
EXPECT_FALSE(actualResult);
|
||||
}
|
||||
|
||||
TEST_F(GameLiftServerManagerTest, StopMatchBackfill_SDKNotInitialized_GetExpectedError)
|
||||
{
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
auto actualResult = m_serverManager->StopMatchBackfill("testticket");
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
||||
EXPECT_FALSE(actualResult);
|
||||
}
|
||||
|
||||
TEST_F(GameLiftServerManagerTest, StopMatchBackfill_CallWithEmptyMatchmakingData_GetExpectedError)
|
||||
{
|
||||
m_serverManager->InitializeGameLiftServerSDK();
|
||||
m_serverManager->SetupTestMatchmakingData("");
|
||||
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
auto actualResult = m_serverManager->StopMatchBackfill("testticket");
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
||||
EXPECT_FALSE(actualResult);
|
||||
}
|
||||
|
||||
TEST_F(GameLiftServerManagerTest, StopMatchBackfill_CallAndSuccessOutcome_GetExpectedResult)
|
||||
{
|
||||
m_serverManager->InitializeGameLiftServerSDK();
|
||||
m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA);
|
||||
|
||||
EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), StopMatchBackfill(testing::_))
|
||||
.Times(1)
|
||||
.WillOnce(Return(Aws::GameLift::GenericOutcome(nullptr)));
|
||||
|
||||
auto actualResult = m_serverManager->StopMatchBackfill("testticket");
|
||||
EXPECT_TRUE(actualResult);
|
||||
}
|
||||
|
||||
TEST_F(GameLiftServerManagerTest, StopMatchBackfill_CallButErrorOutcome_GetExpectedError)
|
||||
{
|
||||
m_serverManager->InitializeGameLiftServerSDK();
|
||||
m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA);
|
||||
|
||||
EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), StopMatchBackfill(testing::_))
|
||||
.Times(1)
|
||||
.WillOnce(Return(Aws::GameLift::GenericOutcome()));
|
||||
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
auto actualResult = m_serverManager->StopMatchBackfill("testticket");
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
||||
EXPECT_FALSE(actualResult);
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AWSGameLiftPlayer.h>
|
||||
#include <AWSGameLiftServerSystemComponent.h>
|
||||
#include <AWSGameLiftServerManager.h>
|
||||
#include <GameLiftServerSDKWrapper.h>
|
||||
@@ -40,17 +41,25 @@ namespace UnitTest
|
||||
|
||||
MOCK_METHOD1(AcceptPlayerSession, GenericOutcome(const std::string&));
|
||||
MOCK_METHOD0(ActivateGameSession, GenericOutcome());
|
||||
MOCK_METHOD1(DescribePlayerSessions, DescribePlayerSessionsOutcome(
|
||||
const Aws::GameLift::Server::Model::DescribePlayerSessionsRequest&));
|
||||
MOCK_METHOD0(InitSDK, Server::InitSDKOutcome());
|
||||
MOCK_METHOD1(ProcessReady, GenericOutcome(const Server::ProcessParameters& processParameters));
|
||||
MOCK_METHOD0(ProcessEnding, GenericOutcome());
|
||||
MOCK_METHOD1(RemovePlayerSession, GenericOutcome(const AZStd::string& playerSessionId));
|
||||
MOCK_METHOD0(GetTerminationTime, AZStd::string());
|
||||
MOCK_METHOD1(StartMatchBackfill, StartMatchBackfillOutcome(
|
||||
const Aws::GameLift::Server::Model::StartMatchBackfillRequest&));
|
||||
MOCK_METHOD1(StopMatchBackfill, GenericOutcome(
|
||||
const Aws::GameLift::Server::Model::StopMatchBackfillRequest&));
|
||||
|
||||
|
||||
GenericOutcome ProcessReadyMock(const Server::ProcessParameters& processParameters)
|
||||
{
|
||||
m_healthCheckFunc = processParameters.getOnHealthCheck();
|
||||
m_onStartGameSessionFunc = processParameters.getOnStartGameSession();
|
||||
m_onProcessTerminateFunc = processParameters.getOnProcessTerminate();
|
||||
m_onUpdateGameSessionFunc = processParameters.getOnUpdateGameSession();
|
||||
|
||||
GenericOutcome successOutcome(nullptr);
|
||||
return successOutcome;
|
||||
@@ -59,6 +68,7 @@ namespace UnitTest
|
||||
AZStd::function<bool()> m_healthCheckFunc;
|
||||
AZStd::function<void()> m_onProcessTerminateFunc;
|
||||
AZStd::function<void(Aws::GameLift::Server::Model::GameSession)> m_onStartGameSessionFunc;
|
||||
AZStd::function<void(Aws::GameLift::Server::Model::UpdateGameSession)> m_onUpdateGameSessionFunc;
|
||||
};
|
||||
|
||||
class AWSGameLiftServerManagerMock
|
||||
@@ -78,12 +88,25 @@ namespace UnitTest
|
||||
m_gameLiftServerSDKWrapperMockPtr = nullptr;
|
||||
}
|
||||
|
||||
void SetupTestMatchmakingData(const AZStd::string& matchmakingData, int maxPlayer = 10)
|
||||
{
|
||||
m_testGameSession.SetMatchmakerData(matchmakingData.c_str());
|
||||
m_testGameSession.SetMaximumPlayerSessionCount(maxPlayer);
|
||||
UpdateGameSessionData(m_testGameSession);
|
||||
}
|
||||
|
||||
bool AddConnectedTestPlayer(const AzFramework::PlayerConnectionConfig& playerConnectionConfig)
|
||||
{
|
||||
return AddConnectedPlayer(playerConnectionConfig);
|
||||
}
|
||||
|
||||
AZStd::vector<AWSGameLiftPlayer> GetTestServerMatchBackfillPlayers()
|
||||
{
|
||||
return GetActiveServerMatchBackfillPlayers();
|
||||
}
|
||||
|
||||
NiceMock<GameLiftServerSDKWrapperMock>* m_gameLiftServerSDKWrapperMockPtr;
|
||||
Aws::GameLift::Server::Model::GameSession m_testGameSession;
|
||||
};
|
||||
|
||||
class AWSGameLiftServerSystemComponentMock
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user