@@ -6,12 +6,7 @@
|
||||
#
|
||||
#
|
||||
|
||||
################################################################################
|
||||
# Atom Renderer: Automated Tests
|
||||
# Runs EditorPythonBindings (hydra) scripts inside the Editor to verify test results for the Atom renderer.
|
||||
################################################################################
|
||||
|
||||
if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED AND AutomatedTesting IN_LIST LY_PROJECTS)
|
||||
if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
ly_add_pytest(
|
||||
NAME AutomatedTesting::Atom_TestSuite_Main
|
||||
TEST_SUITE main
|
||||
|
||||
@@ -25,6 +25,7 @@ TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "tests")
|
||||
class TestAtomEditorComponentsMain(object):
|
||||
"""Holds tests for Atom components."""
|
||||
|
||||
@pytest.mark.xfail(reason="This test is being marked xfail as it failed during an unrelated development run. See LYN-7530 for more details.")
|
||||
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.
|
||||
|
||||
@@ -220,20 +220,18 @@ class TestPerformanceBenchmarkSuite(object):
|
||||
@pytest.mark.system
|
||||
class TestMaterialEditor(object):
|
||||
|
||||
@pytest.mark.parametrize("cfg_args", ["-rhi=dx12", "-rhi=Vulkan"])
|
||||
@pytest.mark.parametrize("cfg_args,expected_lines", [
|
||||
pytest.param("-rhi=dx12", ["Registering dx12 RHI"]),
|
||||
pytest.param("-rhi=Vulkan", ["Registering vulkan RHI"])
|
||||
])
|
||||
@pytest.mark.parametrize("exe_file_name", ["MaterialEditor"])
|
||||
def test_MaterialEditorLaunch_AllRHIOptionsSucceed(
|
||||
self, request, workspace, project, launcher_platform, generic_launcher, exe_file_name, cfg_args):
|
||||
self, request, workspace, project, launcher_platform, generic_launcher, exe_file_name, cfg_args,
|
||||
expected_lines):
|
||||
"""
|
||||
Tests each valid RHI option (Null RHI excluded) can be launched with the MaterialEditor.
|
||||
Checks for the "Finished loading viewport configurations." success message post launch.
|
||||
Checks for the specific expected_lines messaging for each RHI type.
|
||||
"""
|
||||
expected_lines = ["Finished loading viewport configurations."]
|
||||
unexpected_lines = [
|
||||
# "Trace::Assert",
|
||||
# "Trace::Error",
|
||||
"Traceback (most recent call last):",
|
||||
]
|
||||
|
||||
hydra.launch_and_validate_results(
|
||||
request,
|
||||
@@ -243,7 +241,7 @@ class TestMaterialEditor(object):
|
||||
run_python="--runpython",
|
||||
timeout=60,
|
||||
expected_lines=expected_lines,
|
||||
unexpected_lines=unexpected_lines,
|
||||
unexpected_lines=[],
|
||||
halt_on_unexpected=False,
|
||||
null_renderer=False,
|
||||
cfg_args=[cfg_args],
|
||||
|
||||
@@ -14,36 +14,50 @@ from ly_test_tools.o3de.editor_test import EditorSharedTest, EditorTestSuite
|
||||
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
|
||||
class TestAutomation(EditorTestSuite):
|
||||
|
||||
@pytest.mark.test_case_id("C32078118")
|
||||
class AtomEditorComponents_DecalAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_DecalAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C32078119")
|
||||
class AtomEditorComponents_DepthOfFieldAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_DepthOfFieldAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C32078120")
|
||||
class AtomEditorComponents_DirectionalLightAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_DirectionalLightAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C32078121")
|
||||
class AtomEditorComponents_ExposureControlAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_ExposureControlAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C32078115")
|
||||
class AtomEditorComponents_GlobalSkylightIBLAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_GlobalSkylightIBLAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C32078125")
|
||||
class AtomEditorComponents_PhysicalSkyAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_PhysicalSkyAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C32078131")
|
||||
class AtomEditorComponents_PostFXRadiusWeightModifierAdded(EditorSharedTest):
|
||||
from Atom.tests import (
|
||||
hydra_AtomEditorComponents_PostFXRadiusWeightModifierAdded as test_module)
|
||||
|
||||
@pytest.mark.test_case_id("C32078117")
|
||||
class AtomEditorComponents_LightAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_LightAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C36525660")
|
||||
class AtomEditorComponents_DisplayMapperAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_DisplayMapperAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C32078128")
|
||||
class AtomEditorComponents_ReflectionProbeAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_ReflectionProbeAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C32078124")
|
||||
class AtomEditorComponents_MeshAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_MeshAdded as test_module
|
||||
|
||||
class ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(EditorSharedTest):
|
||||
from Atom.tests import hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges as test_module
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
class Tests:
|
||||
creation_undo = (
|
||||
"UNDO Entity creation success",
|
||||
"UNDO Entity creation failed")
|
||||
creation_redo = (
|
||||
"REDO Entity creation success",
|
||||
"REDO Entity creation failed")
|
||||
mesh_entity_creation = (
|
||||
"Mesh Entity successfully created",
|
||||
"Mesh Entity failed to be created")
|
||||
mesh_component_added = (
|
||||
"Entity has a Mesh component",
|
||||
"Entity failed to find Mesh component")
|
||||
mesh_asset_specified = (
|
||||
"Mesh asset set",
|
||||
"Mesh asset not set")
|
||||
enter_game_mode = (
|
||||
"Entered game mode",
|
||||
"Failed to enter game mode")
|
||||
exit_game_mode = (
|
||||
"Exited game mode",
|
||||
"Couldn't exit game mode")
|
||||
is_visible = (
|
||||
"Entity is visible",
|
||||
"Entity was not visible")
|
||||
is_hidden = (
|
||||
"Entity is hidden",
|
||||
"Entity was not hidden")
|
||||
entity_deleted = (
|
||||
"Entity deleted",
|
||||
"Entity was not deleted")
|
||||
deletion_undo = (
|
||||
"UNDO deletion success",
|
||||
"UNDO deletion failed")
|
||||
deletion_redo = (
|
||||
"REDO deletion success",
|
||||
"REDO deletion failed")
|
||||
|
||||
|
||||
def AtomEditorComponents_Mesh_AddedToEntity():
|
||||
"""
|
||||
Summary:
|
||||
Tests the Mesh component can be added to an entity and has the expected functionality.
|
||||
|
||||
Test setup:
|
||||
- Wait for Editor idle loop.
|
||||
- Open the "Base" level.
|
||||
|
||||
Expected Behavior:
|
||||
The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components.
|
||||
Creation and deletion undo/redo should also work.
|
||||
|
||||
Test Steps:
|
||||
1) Create a Mesh entity with no components.
|
||||
2) Add a Mesh component to Mesh entity.
|
||||
3) UNDO the entity creation and component addition.
|
||||
4) REDO the entity creation and component addition.
|
||||
5) Specify the Mesh component asset
|
||||
6) Enter/Exit game mode.
|
||||
7) Test IsHidden.
|
||||
8) Test IsVisible.
|
||||
9) Delete Mesh entity.
|
||||
10) UNDO deletion.
|
||||
11) REDO deletion.
|
||||
12) Look for errors.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
|
||||
from editor_python_test_tools.asset_utils import Asset
|
||||
from editor_python_test_tools.editor_entity_utils import EditorEntity
|
||||
from editor_python_test_tools.utils import Report, Tracer, TestHelper as helper
|
||||
|
||||
with Tracer() as error_tracer:
|
||||
# Test setup begins.
|
||||
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
|
||||
helper.init_idle()
|
||||
helper.open_level("", "Base")
|
||||
|
||||
# Test steps begin.
|
||||
# 1. Create a Mesh entity with no components.
|
||||
mesh_name = "Mesh"
|
||||
mesh_entity = EditorEntity.create_editor_entity(mesh_name)
|
||||
Report.critical_result(Tests.mesh_entity_creation, mesh_entity.exists())
|
||||
|
||||
# 2. Add a Mesh component to Mesh entity.
|
||||
mesh_component = mesh_entity.add_component(mesh_name)
|
||||
Report.critical_result(
|
||||
Tests.mesh_component_added,
|
||||
mesh_entity.has_component(mesh_name))
|
||||
|
||||
# 3. UNDO the entity creation and component addition.
|
||||
# -> UNDO component addition.
|
||||
general.undo()
|
||||
# -> UNDO naming entity.
|
||||
general.undo()
|
||||
# -> UNDO selecting entity.
|
||||
general.undo()
|
||||
# -> UNDO entity creation.
|
||||
general.undo()
|
||||
general.idle_wait_frames(1)
|
||||
Report.result(Tests.creation_undo, not mesh_entity.exists())
|
||||
|
||||
# 4. REDO the entity creation and component addition.
|
||||
# -> REDO entity creation.
|
||||
general.redo()
|
||||
# -> REDO selecting entity.
|
||||
general.redo()
|
||||
# -> REDO naming entity.
|
||||
general.redo()
|
||||
# -> REDO component addition.
|
||||
general.redo()
|
||||
general.idle_wait_frames(1)
|
||||
Report.result(Tests.creation_redo, mesh_entity.exists())
|
||||
|
||||
# 5. Set Mesh component asset property
|
||||
mesh_property_asset = 'Controller|Configuration|Mesh Asset'
|
||||
model_path = os.path.join('Objects', 'shaderball', 'shaderball_default_1m.azmodel')
|
||||
model = Asset.find_asset_by_path(model_path)
|
||||
mesh_component.set_component_property_value(mesh_property_asset, model.id)
|
||||
Report.result(Tests.mesh_asset_specified,
|
||||
mesh_component.get_component_property_value(mesh_property_asset) == model.id)
|
||||
|
||||
# 6. Enter/Exit game mode.
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
general.idle_wait_frames(1)
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
# 7. Test IsHidden.
|
||||
mesh_entity.set_visibility_state(False)
|
||||
Report.result(Tests.is_hidden, mesh_entity.is_hidden() is True)
|
||||
|
||||
# 8. Test IsVisible.
|
||||
mesh_entity.set_visibility_state(True)
|
||||
general.idle_wait_frames(1)
|
||||
Report.result(Tests.is_visible, mesh_entity.is_visible() is True)
|
||||
|
||||
# 9. Delete Mesh entity.
|
||||
mesh_entity.delete()
|
||||
Report.result(Tests.entity_deleted, not mesh_entity.exists())
|
||||
|
||||
# 10. UNDO deletion.
|
||||
general.undo()
|
||||
Report.result(Tests.deletion_undo, mesh_entity.exists())
|
||||
|
||||
# 11. REDO deletion.
|
||||
general.redo()
|
||||
Report.result(Tests.deletion_redo, not mesh_entity.exists())
|
||||
|
||||
# 12. Look for errors or asserts.
|
||||
helper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
|
||||
for error_info in error_tracer.errors:
|
||||
Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
|
||||
for assert_info in error_tracer.asserts:
|
||||
Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(AtomEditorComponents_Mesh_AddedToEntity)
|
||||
@@ -173,6 +173,7 @@ namespace AZ
|
||||
|
||||
if (assetTracker)
|
||||
{
|
||||
assetTracker->FixUpAsset(*instance);
|
||||
assetTracker->AddAsset(*instance);
|
||||
}
|
||||
|
||||
@@ -185,7 +186,20 @@ namespace AZ
|
||||
return context.Report(result, message);
|
||||
}
|
||||
|
||||
void SerializedAssetTracker::AddAsset(Asset<AssetData>& asset)
|
||||
void SerializedAssetTracker::SetAssetFixUp(AssetFixUp assetFixUpCallback)
|
||||
{
|
||||
m_assetFixUpCallback = AZStd::move(assetFixUpCallback);
|
||||
}
|
||||
|
||||
void SerializedAssetTracker::FixUpAsset(Asset<AssetData>& asset)
|
||||
{
|
||||
if (m_assetFixUpCallback)
|
||||
{
|
||||
m_assetFixUpCallback(asset);
|
||||
}
|
||||
}
|
||||
|
||||
void SerializedAssetTracker::AddAsset(Asset<AssetData> asset)
|
||||
{
|
||||
m_serializedAssets.emplace_back(asset);
|
||||
}
|
||||
@@ -199,5 +213,6 @@ namespace AZ
|
||||
{
|
||||
return m_serializedAssets;
|
||||
}
|
||||
|
||||
} // namespace Data
|
||||
} // namespace AZ
|
||||
|
||||
@@ -39,13 +39,18 @@ namespace AZ
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(SerializedAssetTracker, "{1E067091-8C0A-44B1-A455-6E97663F6963}");
|
||||
using AssetFixUp = AZStd::function<void(Asset<AssetData>& asset)>;
|
||||
|
||||
void AddAsset(Asset<AssetData>& asset);
|
||||
void SetAssetFixUp(AssetFixUp assetFixUpCallback);
|
||||
void FixUpAsset(Asset<AssetData>& asset);
|
||||
|
||||
void AddAsset(Asset<AssetData> asset);
|
||||
AZStd::vector<Asset<AssetData>>& GetTrackedAssets();
|
||||
const AZStd::vector<Asset<AssetData>>& GetTrackedAssets() const;
|
||||
|
||||
private:
|
||||
AZStd::vector<Asset<AssetData>> m_serializedAssets;
|
||||
AssetFixUp m_assetFixUpCallback;
|
||||
};
|
||||
} // namespace Data
|
||||
} // namespace AZ
|
||||
|
||||
@@ -476,15 +476,16 @@ namespace AZ
|
||||
|
||||
// Responsible for using the Json Serialization Issue Callback system
|
||||
// to determine when a JSON Patch or JSON Merge Patch modifies a value
|
||||
// at a path underneath the IConsole::ConsoleRootCommandKey JSON pointer
|
||||
// at a path underneath the IConsole::ConsoleRuntimeCommandKey JSON pointer
|
||||
JsonSerializationResult::ResultCode operator()(AZStd::string_view message,
|
||||
JsonSerializationResult::ResultCode result, AZStd::string_view path)
|
||||
{
|
||||
AZ::IO::PathView consoleRootCommandKey{ IConsole::ConsoleRootCommandKey, AZ::IO::PosixPathSeparator };
|
||||
constexpr AZ::IO::PathView consoleRootCommandKey{ IConsole::ConsoleRuntimeCommandKey, AZ::IO::PosixPathSeparator };
|
||||
constexpr AZ::IO::PathView consoleAutoexecCommandKey{ IConsole::ConsoleAutoexecCommandKey, AZ::IO::PosixPathSeparator };
|
||||
AZ::IO::PathView inputKey{ path, AZ::IO::PosixPathSeparator };
|
||||
if (result.GetTask() == JsonSerializationResult::Tasks::Merge
|
||||
&& result.GetProcessing() == JsonSerializationResult::Processing::Completed
|
||||
&& inputKey.IsRelativeTo(consoleRootCommandKey))
|
||||
&& (inputKey.IsRelativeTo(consoleRootCommandKey) || inputKey.IsRelativeTo(consoleAutoexecCommandKey)))
|
||||
{
|
||||
if (auto type = m_settingsRegistry.GetType(path); type != SettingsRegistryInterface::Type::NoType)
|
||||
{
|
||||
@@ -510,12 +511,24 @@ namespace AZ
|
||||
{
|
||||
using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
|
||||
|
||||
AZ::IO::PathView consoleRootCommandKey{ IConsole::ConsoleRootCommandKey, AZ::IO::PosixPathSeparator };
|
||||
constexpr AZ::IO::PathView consoleRuntimeCommandKey{ IConsole::ConsoleRuntimeCommandKey, AZ::IO::PosixPathSeparator };
|
||||
constexpr AZ::IO::PathView consoleAutoexecCommandKey{ IConsole::ConsoleAutoexecCommandKey, AZ::IO::PosixPathSeparator };
|
||||
AZ::IO::PathView inputKey{ path, AZ::IO::PosixPathSeparator };
|
||||
// The ConsoleRootComamndKey is not a command itself so strictly children keys are being examined
|
||||
if (inputKey.IsRelativeTo(consoleRootCommandKey) && inputKey != consoleRootCommandKey)
|
||||
|
||||
// Abuses the IsRelativeToFuncton function of the path class to extract the console
|
||||
// command from the settings registry objects
|
||||
FixedValueString command;
|
||||
if (inputKey != consoleRuntimeCommandKey && inputKey.IsRelativeTo(consoleRuntimeCommandKey))
|
||||
{
|
||||
command = inputKey.LexicallyRelative(consoleRuntimeCommandKey).Native();
|
||||
}
|
||||
else if (inputKey != consoleAutoexecCommandKey && inputKey.IsRelativeTo(consoleAutoexecCommandKey))
|
||||
{
|
||||
command = inputKey.LexicallyRelative(consoleAutoexecCommandKey).Native();
|
||||
}
|
||||
|
||||
if (!command.empty())
|
||||
{
|
||||
FixedValueString command = inputKey.LexicallyRelative(consoleRootCommandKey).Native();
|
||||
ConsoleCommandContainer commandArgs;
|
||||
// Argument string which stores the value from the Settings Registry long enough
|
||||
// to pass into the PerformCommand. The ConsoleCommandContainer stores string_views
|
||||
@@ -603,9 +616,10 @@ namespace AZ
|
||||
|
||||
void Console::RegisterCommandInvokerWithSettingsRegistry(AZ::SettingsRegistryInterface& settingsRegistry)
|
||||
{
|
||||
// Make sure the there is a JSON object at the path of AZ::IConsole::ConsoleRootCommandKey
|
||||
// Make sure the there is a JSON object at the ConsoleRuntimeCommandKey or ConsoleAutoexecKey
|
||||
// So that JSON Patch is able to add values underneath that object (JSON Patch doesn't create intermediate objects)
|
||||
settingsRegistry.MergeSettings(R"({ "Amazon": { "AzCore": { "Runtime": { "ConsoleCommands": {} } }}})",
|
||||
settingsRegistry.MergeSettings(R"({ "Amazon": { "AzCore": { "Runtime": { "ConsoleCommands": {} } } })"
|
||||
R"(,"O3DE": { "Autoexec": { "ConsoleCommands": {} } } })",
|
||||
SettingsRegistryInterface::Format::JsonMergePatch);
|
||||
m_consoleCommandKeyHandler = settingsRegistry.RegisterNotifier(ConsoleCommandKeyNotificationHandler{ settingsRegistry, *this });
|
||||
|
||||
|
||||
@@ -31,7 +31,8 @@ namespace AZ
|
||||
|
||||
using FunctorVisitor = AZStd::function<void(ConsoleFunctorBase*)>;
|
||||
|
||||
inline static constexpr AZStd::string_view ConsoleRootCommandKey = "/Amazon/AzCore/Runtime/ConsoleCommands";
|
||||
inline static constexpr AZStd::string_view ConsoleRuntimeCommandKey = "/Amazon/AzCore/Runtime/ConsoleCommands";
|
||||
inline static constexpr AZStd::string_view ConsoleAutoexecCommandKey = "/O3DE/Autoexec/ConsoleCommands";
|
||||
|
||||
IConsole() = default;
|
||||
virtual ~IConsole() = default;
|
||||
|
||||
@@ -160,8 +160,8 @@ namespace AZ
|
||||
/**
|
||||
* Locking primitive that is used when executing events in the event queue.
|
||||
*/
|
||||
using EventQueueMutexType = typename AZStd::Utils::if_c<AZStd::is_same<typename Traits::EventQueueMutexType, NullMutex>::value, // if EventQueueMutexType==NullMutex use MutexType otherwise EventQueueMutexType
|
||||
MutexType, typename Traits::EventQueueMutexType>::type;
|
||||
using EventQueueMutexType = AZStd::conditional_t<AZStd::is_same<typename Traits::EventQueueMutexType, NullMutex>::value, // if EventQueueMutexType==NullMutex use MutexType otherwise EventQueueMutexType
|
||||
MutexType, typename Traits::EventQueueMutexType>;
|
||||
|
||||
/**
|
||||
* Pointer to an address on the bus.
|
||||
@@ -180,14 +180,22 @@ namespace AZ
|
||||
* `<BusName>::ExecuteQueuedEvents()`.
|
||||
* By default, the event queue is disabled.
|
||||
*/
|
||||
static const bool EnableEventQueue = Traits::EnableEventQueue;
|
||||
static const bool EventQueueingActiveByDefault = Traits::EventQueueingActiveByDefault;
|
||||
static const bool EnableQueuedReferences = Traits::EnableQueuedReferences;
|
||||
static constexpr bool EnableEventQueue = Traits::EnableEventQueue;
|
||||
static constexpr bool EventQueueingActiveByDefault = Traits::EventQueueingActiveByDefault;
|
||||
static constexpr bool EnableQueuedReferences = Traits::EnableQueuedReferences;
|
||||
|
||||
/**
|
||||
* True if the EBus supports more than one address. Otherwise, false.
|
||||
*/
|
||||
static const bool HasId = Traits::AddressPolicy != EBusAddressPolicy::Single;
|
||||
static constexpr bool HasId = Traits::AddressPolicy != EBusAddressPolicy::Single;
|
||||
|
||||
/**
|
||||
* Template Lock Guard class that wraps around the Mutex
|
||||
* The EBus uses for Dispatching Events.
|
||||
* This is not the EBus Context Mutex if LocklessDispatch is true
|
||||
*/
|
||||
template <typename DispatchMutex>
|
||||
using DispatchLockGuard = typename Traits::template DispatchLockGuard<DispatchMutex, Traits::LocklessDispatch>;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -460,7 +468,7 @@ namespace AZ
|
||||
using BusPtr = typename Traits::BusPtr;
|
||||
|
||||
/**
|
||||
* Helper to queue an event by BusIdType only when function queueing is enabled
|
||||
* Helper to queue an event by BusIdType only when function queueing is enabled
|
||||
* @param id Address ID. Handlers that are connected to this ID will receive the event.
|
||||
* @param func Function pointer of the event to dispatch.
|
||||
* @param args Function arguments that are passed to each handler.
|
||||
@@ -581,7 +589,7 @@ namespace AZ
|
||||
, public EBusBroadcaster<Bus, Traits>
|
||||
, public EBusEventer<Bus, Traits>
|
||||
, public EBusEventEnumerator<Bus, Traits>
|
||||
, public AZStd::Utils::if_c<Traits::EnableEventQueue, EBusEventQueue<Bus, Traits>, EBusNullQueue>::type
|
||||
, public AZStd::conditional_t<Traits::EnableEventQueue, EBusEventQueue<Bus, Traits>, EBusNullQueue>
|
||||
{
|
||||
};
|
||||
|
||||
@@ -599,7 +607,7 @@ namespace AZ
|
||||
: public EventDispatcher<Bus, Traits>
|
||||
, public EBusBroadcaster<Bus, Traits>
|
||||
, public EBusBroadcastEnumerator<Bus, Traits>
|
||||
, public AZStd::Utils::if_c<Traits::EnableEventQueue, EBusBroadcastQueue<Bus, Traits>, EBusNullQueue>::type
|
||||
, public AZStd::conditional_t<Traits::EnableEventQueue, EBusBroadcastQueue<Bus, Traits>, EBusNullQueue>
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
@@ -236,6 +236,17 @@ namespace AZ
|
||||
* code before or after an event.
|
||||
*/
|
||||
using EventProcessingPolicy = EBusEventProcessingPolicy;
|
||||
|
||||
/**
|
||||
* Template Lock Guard class that wraps around the Mutex
|
||||
* The EBus Context uses the LockGuard when dispatching
|
||||
* (either AZStd::scoped_lock<MutexType> or NullLockGuard<MutexType>)
|
||||
* The IsLocklessDispatch bool is there to defer evaluation of the LocklessDispatch constant
|
||||
* Otherwise the value above in EBusTraits.h is always used and not the value
|
||||
* that the derived trait class sets.
|
||||
*/
|
||||
template <typename DispatchMutex, bool IsLocklessDispatch>
|
||||
using DispatchLockGuard = AZStd::conditional_t<IsLocklessDispatch, AZ::Internal::NullLockGuard<DispatchMutex>, AZStd::scoped_lock<DispatchMutex>>;
|
||||
};
|
||||
|
||||
namespace Internal
|
||||
@@ -496,6 +507,14 @@ namespace AZ
|
||||
*/
|
||||
static const bool HasId = Traits::AddressPolicy != EBusAddressPolicy::Single;
|
||||
|
||||
/**
|
||||
* Template Lock Guard class that wraps around the Mutex
|
||||
* The EBus uses for Dispatching Events.
|
||||
* This is not EBus Context Mutex when LocklessDispatch is set
|
||||
*/
|
||||
template <typename DispatchMutex>
|
||||
using DispatchLockGuard = typename ImplTraits::template DispatchLockGuard<DispatchMutex>;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Check to help identify common mistakes
|
||||
/// @cond EXCLUDE_DOCS
|
||||
@@ -620,11 +639,11 @@ namespace AZ
|
||||
using ContextMutexType = AZStd::conditional_t<BusTraits::LocklessDispatch && AZStd::is_same_v<MutexType, AZ::NullMutex>, AZStd::shared_mutex, MutexType>;
|
||||
|
||||
/**
|
||||
* The scoped lock guard to use (either AZStd::scoped_lock<MutexType> or NullLockGuard<MutexType>
|
||||
* The scoped lock guard to use
|
||||
* during broadcast/event dispatch.
|
||||
* @see EBusTraits::LocklessDispatch
|
||||
*/
|
||||
using DispatchLockGuard = AZStd::conditional_t<BusTraits::LocklessDispatch, AZ::Internal::NullLockGuard<ContextMutexType>, AZStd::scoped_lock<ContextMutexType>>;
|
||||
using DispatchLockGuard = DispatchLockGuard<ContextMutexType>;
|
||||
|
||||
/**
|
||||
* The scoped lock guard to use during connection. Some specialized policies execute handler methods which
|
||||
@@ -704,6 +723,11 @@ namespace AZ
|
||||
static Context& GetOrCreateContext(bool trackCallstack=true);
|
||||
|
||||
static bool IsInDispatch(Context* context = GetContext(false));
|
||||
|
||||
/**
|
||||
* Returns whether the EBus context is in the middle of a dispatch on the current thread
|
||||
*/
|
||||
static bool IsInDispatchThisThread(Context* context = GetContext(false));
|
||||
/// @cond EXCLUDE_DOCS
|
||||
struct RouterCallstackEntry
|
||||
: public CallstackEntry
|
||||
@@ -1208,6 +1232,13 @@ AZ_POP_DISABLE_WARNING
|
||||
return context != nullptr && context->m_dispatches > 0;
|
||||
}
|
||||
|
||||
template<class Interface, class Traits>
|
||||
bool EBus<Interface, Traits>::IsInDispatchThisThread(Context* context)
|
||||
{
|
||||
return context != nullptr && context->s_callstack != nullptr
|
||||
&& context->s_callstack->m_prev != nullptr;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
template<class Interface, class Traits>
|
||||
EBus<Interface, Traits>::RouterCallstackEntry::RouterCallstackEntry(Iterator it, const BusIdType* busId, bool isQueued, bool isReverse)
|
||||
|
||||
@@ -204,14 +204,14 @@ namespace AZ
|
||||
[[nodiscard]] virtual PreMergeEventHandler RegisterPreMergeEvent(const PreMergeEventCallback& callback) = 0;
|
||||
//! Register a function that will be called before a file is merged.
|
||||
//! @callback The function to call before a file is merged.
|
||||
[[nodiscard]] virtual PreMergeEventHandler RegisterPreMergeEvent (PreMergeEventCallback&& callback) = 0;
|
||||
[[nodiscard]] virtual PreMergeEventHandler RegisterPreMergeEvent(PreMergeEventCallback&& callback) = 0;
|
||||
|
||||
//! Register a function that will be called after a file is merged.
|
||||
//! @callback The function to call after a file is merged.
|
||||
[[nodiscard]] virtual PostMergeEventHandler RegisterPostMergeEvent(const PostMergeEventCallback& callback) = 0;
|
||||
//! Register a function that will be called after a file is merged.
|
||||
//! @callback The function to call after a file is merged.
|
||||
[[nodiscard]] virtual PostMergeEventHandler RegisterPostMergeEvent (PostMergeEventCallback&& callback) = 0;
|
||||
[[nodiscard]] virtual PostMergeEventHandler RegisterPostMergeEvent(PostMergeEventCallback&& callback) = 0;
|
||||
|
||||
//! Gets the boolean value at the provided path.
|
||||
//! @param result The target to write the result to.
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* 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/Settings/SettingsRegistryVisitorUtils.h>
|
||||
|
||||
|
||||
namespace AZ::SettingsRegistryVisitorUtils
|
||||
{
|
||||
// Field Visitor implementation
|
||||
FieldVisitor::FieldVisitor() = default;
|
||||
FieldVisitor::FieldVisitor(VisitFieldType visitFieldType)
|
||||
: m_visitFieldType{ visitFieldType }
|
||||
{
|
||||
}
|
||||
|
||||
auto FieldVisitor::Traverse(AZStd::string_view path, AZStd::string_view valueName,
|
||||
VisitAction action, Type type) -> VisitResponse
|
||||
{
|
||||
// A default response skip prevents visiting grand children(depth 2 or lower)
|
||||
VisitResponse visitResponse = VisitResponse::Skip;
|
||||
if (action == VisitAction::Begin)
|
||||
{
|
||||
// Invoke FieldVisitor override if the root path has been set
|
||||
if (m_rootPath.has_value())
|
||||
{
|
||||
Visit(path, valueName, type);
|
||||
}
|
||||
// To make sure only the direct children are visited(depth 1)
|
||||
// set the root path once and set the VisitReponsoe
|
||||
// to Continue to recurse into is fields
|
||||
if (!m_rootPath.has_value())
|
||||
{
|
||||
bool visitableFieldType{};
|
||||
switch (m_visitFieldType)
|
||||
{
|
||||
case VisitFieldType::Array:
|
||||
visitableFieldType = type == Type::Array;
|
||||
break;
|
||||
case VisitFieldType::Object:
|
||||
visitableFieldType = type == Type::Object;
|
||||
break;
|
||||
case VisitFieldType::ArrayOrObject:
|
||||
visitableFieldType = type == Type::Array || type ==Type::Object;
|
||||
break;
|
||||
default:
|
||||
AZ_Error("FieldVisitor", false, "The field visitation type value is invalid");
|
||||
break;
|
||||
}
|
||||
|
||||
if (visitableFieldType)
|
||||
{
|
||||
m_rootPath = path;
|
||||
visitResponse = VisitResponse::Continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (action == VisitAction::Value)
|
||||
{
|
||||
// Invoke FieldVisitor override if the root path has been set
|
||||
if (m_rootPath.has_value())
|
||||
{
|
||||
Visit(path, valueName, type);
|
||||
}
|
||||
}
|
||||
else if (action == VisitAction::End)
|
||||
{
|
||||
// Reset m_rootPath back to null when the root path has finished being visited
|
||||
if (m_rootPath.has_value() && *m_rootPath == path)
|
||||
{
|
||||
m_rootPath = AZStd::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return visitResponse;
|
||||
}
|
||||
|
||||
// Array Visitor implementation
|
||||
ArrayVisitor::ArrayVisitor()
|
||||
: FieldVisitor(VisitFieldType::Array)
|
||||
{
|
||||
}
|
||||
|
||||
// Object Visitor implementation
|
||||
ObjectVisitor::ObjectVisitor()
|
||||
: FieldVisitor(VisitFieldType::Object)
|
||||
{
|
||||
}
|
||||
|
||||
// Generic VisitField Callback implemention
|
||||
template <typename BaseVisitor>
|
||||
bool VisitFieldCallback(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path)
|
||||
{
|
||||
struct VisitFieldVisitor
|
||||
: BaseVisitor
|
||||
{
|
||||
using BaseVisitor::Visit;
|
||||
VisitFieldVisitor(const VisitorCallback& visitCallback)
|
||||
: m_visitCallback{ visitCallback }
|
||||
{}
|
||||
|
||||
void Visit(AZStd::string_view path, AZStd::string_view fieldIndex, typename BaseVisitor::Type type) override
|
||||
{
|
||||
m_visitCallback(path, fieldIndex, type);
|
||||
}
|
||||
|
||||
const VisitorCallback& m_visitCallback;
|
||||
};
|
||||
|
||||
VisitFieldVisitor visitor{ visitCallback };
|
||||
return settingsRegistry.Visit(visitor, path);
|
||||
}
|
||||
|
||||
// VisitField implementation
|
||||
bool VisitField(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path)
|
||||
{
|
||||
return VisitFieldCallback<FieldVisitor>(settingsRegistry, visitCallback, path);
|
||||
}
|
||||
|
||||
// VisitArray implementation
|
||||
bool VisitArray(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path)
|
||||
{
|
||||
return VisitFieldCallback<ArrayVisitor>(settingsRegistry, visitCallback, path);
|
||||
}
|
||||
|
||||
// VisitObject implementation
|
||||
bool VisitObject(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path)
|
||||
{
|
||||
return VisitFieldCallback<ObjectVisitor>(settingsRegistry, visitCallback, path);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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/Settings/SettingsRegistry.h>
|
||||
|
||||
|
||||
namespace AZ::SettingsRegistryVisitorUtils
|
||||
{
|
||||
//! Interface for visiting the fields of an array or object
|
||||
//! To access the values, use the SettingsRegistryInterface Get/GetObject methods
|
||||
struct FieldVisitor
|
||||
: public AZ::SettingsRegistryInterface::Visitor
|
||||
{
|
||||
using VisitResponse = AZ::SettingsRegistryInterface::VisitResponse;
|
||||
using VisitAction = AZ::SettingsRegistryInterface::VisitAction;
|
||||
using Type = AZ::SettingsRegistryInterface::Type;
|
||||
|
||||
FieldVisitor();
|
||||
|
||||
// Bring the base class visitor functions into scope
|
||||
using AZ::SettingsRegistryInterface::Visitor::Visit;
|
||||
virtual void Visit(AZStd::string_view path, AZStd::string_view arrayIndex, Type type) = 0;
|
||||
|
||||
protected:
|
||||
// VisitFieldType is used for filtering the type of referenced by the root path
|
||||
enum class VisitFieldType
|
||||
{
|
||||
Array,
|
||||
Object,
|
||||
ArrayOrObject
|
||||
};
|
||||
FieldVisitor(const VisitFieldType visitFieldType);
|
||||
private:
|
||||
VisitResponse Traverse(AZStd::string_view path, AZStd::string_view valueName,
|
||||
VisitAction action, Type type) override;
|
||||
|
||||
VisitFieldType m_visitFieldType{ VisitFieldType::ArrayOrObject };
|
||||
AZStd::optional<AZ::SettingsRegistryInterface::FixedValueString> m_rootPath;
|
||||
};
|
||||
|
||||
//! Interface for visiting the fields of an array
|
||||
//! To access the values, use the SettingsRegistryInterface Get/GetObject methods
|
||||
struct ArrayVisitor
|
||||
: public FieldVisitor
|
||||
{
|
||||
ArrayVisitor();
|
||||
};
|
||||
|
||||
//! Interface for visiting the fields of an object
|
||||
//! To access the values, use the SettingsRegistryInterface Get/GetObject methods
|
||||
struct ObjectVisitor
|
||||
: public FieldVisitor
|
||||
{
|
||||
ObjectVisitor();
|
||||
};
|
||||
|
||||
//! Signature of callback funcition invoked when visiting an element of an array or object
|
||||
using VisitorCallback = AZStd::function<void(AZStd::string_view path, AZStd::string_view fieldName,
|
||||
AZ::SettingsRegistryInterface::Type)>;
|
||||
|
||||
//! Invokes the visitor callback for each element of either the array or object at @path
|
||||
//! If @path is not an array or object, then no elements are visited
|
||||
//! This function will not recurse into children of elements
|
||||
//! @visitCallback functor that is invoked for each array or object element found
|
||||
bool VisitField(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path);
|
||||
//! Invokes the visitor callback for each element of the array at @path
|
||||
//! If @path is not an array, then no elements are visited
|
||||
//! This function will not recurse into children of elements
|
||||
//! @visitCallback functor that is invoked for each array element found
|
||||
bool VisitArray(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path);
|
||||
//! Invokes the visitor callback for each element of the object at @path
|
||||
//! If @path is not an object, then no elements are visited
|
||||
//! This function will not recurse into children of elements
|
||||
//! @visitCallback functor that is invoked for each object element found
|
||||
bool VisitObject(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path);
|
||||
}
|
||||
@@ -566,6 +566,8 @@ set(FILES
|
||||
Settings/SettingsRegistryMergeUtils.h
|
||||
Settings/SettingsRegistryScriptUtils.cpp
|
||||
Settings/SettingsRegistryScriptUtils.h
|
||||
Settings/SettingsRegistryVisitorUtils.cpp
|
||||
Settings/SettingsRegistryVisitorUtils.h
|
||||
State/HSM.cpp
|
||||
State/HSM.h
|
||||
Statistics/NamedRunningStatistic.h
|
||||
|
||||
@@ -2088,7 +2088,7 @@ namespace UnitTest
|
||||
DisconnectNextHandlerByIdImpl multiHandler2;
|
||||
multiHandler2.BusConnect(DisconnectNextHandlerByIdImpl::firstBusAddress);
|
||||
multiHandler2.BusConnect(DisconnectNextHandlerByIdImpl::secondBusAddress);
|
||||
|
||||
|
||||
// Set the first handler m_nextHandler field to point to the second handler
|
||||
multiHandler1.m_nextHandler = &multiHandler2;
|
||||
|
||||
@@ -2807,7 +2807,7 @@ namespace UnitTest
|
||||
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(m_val % m_maxSleep));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DoConnect() override
|
||||
{
|
||||
MyEventGroupBus::Handler::BusConnect(m_id);
|
||||
@@ -2854,7 +2854,7 @@ namespace UnitTest
|
||||
}
|
||||
|
||||
MyEventGroupBus::Event(id, &MyEventGroupBus::Events::Calculate, i, i * 2, i << 4);
|
||||
|
||||
|
||||
LocklessConnectorBus::Event(id, &LocklessConnectorBus::Events::DoDisconnect);
|
||||
|
||||
bool failed = (AZStd::find_if(&sentinel[0], end, [](char s) { return s != 0; }) != end);
|
||||
@@ -2891,7 +2891,7 @@ namespace UnitTest
|
||||
{
|
||||
MyEventGroupImpl()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
~MyEventGroupImpl() override
|
||||
@@ -3614,7 +3614,7 @@ namespace UnitTest
|
||||
{
|
||||
AZStd::this_thread::yield();
|
||||
}
|
||||
|
||||
|
||||
EXPECT_GE(AZStd::chrono::system_clock::now(), endTime);
|
||||
};
|
||||
AZStd::thread connectThread([&connectHandler, &waitHandler]()
|
||||
@@ -3813,7 +3813,7 @@ namespace UnitTest
|
||||
struct LastHandlerDisconnectHandler
|
||||
: public LastHandlerDisconnectBus::Handler
|
||||
{
|
||||
void OnEvent() override
|
||||
void OnEvent() override
|
||||
{
|
||||
++m_numOnEvents;
|
||||
BusDisconnect();
|
||||
@@ -3854,7 +3854,7 @@ namespace UnitTest
|
||||
struct DisconnectAssertHandler
|
||||
: public DisconnectAssertBus::Handler
|
||||
{
|
||||
|
||||
|
||||
};
|
||||
|
||||
TEST_F(EBus, HandlerDestroyedWithoutDisconnect_Asserts)
|
||||
@@ -3995,6 +3995,191 @@ namespace UnitTest
|
||||
|
||||
idTestRequest.Disconnect();
|
||||
}
|
||||
|
||||
// IsInDispatchThisThread
|
||||
struct IsInThreadDispatchRequests
|
||||
: AZ::EBusTraits
|
||||
{
|
||||
using MutexType = AZStd::recursive_mutex;
|
||||
};
|
||||
|
||||
using IsInThreadDispatchBus = AZ::EBus<IsInThreadDispatchRequests>;
|
||||
|
||||
class IsInThreadDispatchHandler
|
||||
: public IsInThreadDispatchBus::Handler
|
||||
{};
|
||||
|
||||
TEST_F(EBus, InvokingIsInThisThread_ReturnsSuccess_OnlyIfThreadIsInDispatch)
|
||||
{
|
||||
IsInThreadDispatchHandler handler;
|
||||
handler.BusConnect();
|
||||
|
||||
auto ThreadDispatcher = [](IsInThreadDispatchRequests*)
|
||||
{
|
||||
EXPECT_TRUE(IsInThreadDispatchBus::IsInDispatchThisThread());
|
||||
auto PerThreadBusDispatch = []()
|
||||
{
|
||||
EXPECT_FALSE(IsInThreadDispatchBus::IsInDispatchThisThread());
|
||||
};
|
||||
AZStd::array threads{ AZStd::thread(PerThreadBusDispatch), AZStd::thread(PerThreadBusDispatch) };
|
||||
for (AZStd::thread& thread : threads)
|
||||
{
|
||||
thread.join();
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr size_t ThreadDispatcherIterations = 4;
|
||||
for (size_t iteration = 0; iteration < ThreadDispatcherIterations; ++iteration)
|
||||
{
|
||||
EXPECT_FALSE(IsInThreadDispatchBus::IsInDispatchThisThread());
|
||||
IsInThreadDispatchBus::Broadcast(ThreadDispatcher);
|
||||
EXPECT_FALSE(IsInThreadDispatchBus::IsInDispatchThisThread());
|
||||
}
|
||||
}
|
||||
|
||||
// Thread Dispatch Policy
|
||||
struct ThreadDispatchTestBusTraits
|
||||
: AZ::EBusTraits
|
||||
{
|
||||
using MutexType = AZStd::recursive_mutex;
|
||||
|
||||
struct PostThreadDispatchTestInvoker
|
||||
{
|
||||
~PostThreadDispatchTestInvoker();
|
||||
};
|
||||
|
||||
template <typename DispatchMutex>
|
||||
struct ThreadDispatchTestLockGuard
|
||||
{
|
||||
ThreadDispatchTestLockGuard(DispatchMutex& contextMutex)
|
||||
: m_lock{ contextMutex }
|
||||
{}
|
||||
ThreadDispatchTestLockGuard(DispatchMutex& contextMutex, AZStd::adopt_lock_t adopt_lock)
|
||||
: m_lock{ contextMutex, adopt_lock }
|
||||
{}
|
||||
ThreadDispatchTestLockGuard(const ThreadDispatchTestLockGuard&) = delete;
|
||||
ThreadDispatchTestLockGuard& operator=(const ThreadDispatchTestLockGuard&) = delete;
|
||||
private:
|
||||
PostThreadDispatchTestInvoker m_threadPolicyInvoker;
|
||||
using LockType = AZStd::conditional_t<LocklessDispatch, AZ::Internal::NullLockGuard<DispatchMutex>, AZStd::scoped_lock<DispatchMutex>>;
|
||||
LockType m_lock;
|
||||
};
|
||||
|
||||
template <typename DispatchMutex, bool IsLocklessDispatch>
|
||||
using DispatchLockGuard = ThreadDispatchTestLockGuard<DispatchMutex>;
|
||||
|
||||
static inline AZStd::atomic<int32_t> s_threadPostDispatchCalls;
|
||||
};
|
||||
|
||||
class ThreadDispatchTestRequests
|
||||
{
|
||||
public:
|
||||
virtual void FirstCall() = 0;
|
||||
virtual void SecondCall() = 0;
|
||||
virtual void ThirdCall() = 0;
|
||||
};
|
||||
|
||||
using ThreadDispatchTestBus = AZ::EBus<ThreadDispatchTestRequests, ThreadDispatchTestBusTraits>;
|
||||
|
||||
ThreadDispatchTestBusTraits::PostThreadDispatchTestInvoker::~PostThreadDispatchTestInvoker()
|
||||
{
|
||||
if (!ThreadDispatchTestBus::IsInDispatchThisThread())
|
||||
{
|
||||
++s_threadPostDispatchCalls;
|
||||
}
|
||||
}
|
||||
|
||||
class ThreadDispatchTestHandler
|
||||
: public ThreadDispatchTestBus::Handler
|
||||
{
|
||||
public:
|
||||
void Connect()
|
||||
{
|
||||
ThreadDispatchTestBus::Handler::BusConnect();
|
||||
}
|
||||
void Disconnect()
|
||||
{
|
||||
ThreadDispatchTestBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void FirstCall() override
|
||||
{
|
||||
ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::SecondCall);
|
||||
}
|
||||
void SecondCall() override
|
||||
{
|
||||
ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::ThirdCall);
|
||||
}
|
||||
void ThirdCall() override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ParamType>
|
||||
class EBusParamFixture
|
||||
: public ScopedAllocatorSetupFixture
|
||||
, public ::testing::WithParamInterface<ParamType>
|
||||
{};
|
||||
|
||||
struct ThreadDispatchParams
|
||||
{
|
||||
size_t m_threadCount{};
|
||||
size_t m_handlerCount{};
|
||||
};
|
||||
|
||||
using ThreadDispatchParamFixture = EBusParamFixture<ThreadDispatchParams>;
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
ThreadDispatch,
|
||||
ThreadDispatchParamFixture,
|
||||
::testing::Values(
|
||||
ThreadDispatchParams{ 1, 1 },
|
||||
ThreadDispatchParams{ 2, 1 },
|
||||
ThreadDispatchParams{ 1, 2 },
|
||||
ThreadDispatchParams{ 2, 2 },
|
||||
ThreadDispatchParams{ 16, 8 }
|
||||
)
|
||||
);
|
||||
|
||||
TEST_P(ThreadDispatchParamFixture, CustomDispatchLockGuard_InvokesPostDispatchFunction_AfterThreadHasFinishedDispatch)
|
||||
{
|
||||
ThreadDispatchTestBusTraits::s_threadPostDispatchCalls = 0;
|
||||
ThreadDispatchParams threadDispatchParams = GetParam();
|
||||
AZStd::vector<AZStd::thread> testThreads;
|
||||
AZStd::vector<ThreadDispatchTestHandler> testHandlers(threadDispatchParams.m_handlerCount);
|
||||
for (ThreadDispatchTestHandler& testHandler : testHandlers)
|
||||
{
|
||||
testHandler.Connect();
|
||||
}
|
||||
|
||||
static constexpr size_t DispatchThreadCalls = 3;
|
||||
const size_t totalThreadDispatchCalls = threadDispatchParams.m_threadCount * DispatchThreadCalls;
|
||||
|
||||
auto DispatchThreadWorker = []()
|
||||
{
|
||||
ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::FirstCall);
|
||||
ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::SecondCall);
|
||||
ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::ThirdCall);
|
||||
};
|
||||
|
||||
for (size_t threadIndex = 0; threadIndex < threadDispatchParams.m_threadCount; ++threadIndex)
|
||||
{
|
||||
testThreads.emplace_back(DispatchThreadWorker);
|
||||
}
|
||||
|
||||
for (AZStd::thread& thread : testThreads)
|
||||
{
|
||||
thread.join();
|
||||
}
|
||||
|
||||
for (ThreadDispatchTestHandler& testHandler : testHandlers)
|
||||
{
|
||||
testHandler.Disconnect();
|
||||
}
|
||||
|
||||
EXPECT_EQ(totalThreadDispatchCalls, ThreadDispatchTestBusTraits::s_threadPostDispatchCalls);
|
||||
ThreadDispatchTestBusTraits::s_threadPostDispatchCalls = 0;
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
#if defined(HAVE_BENCHMARK)
|
||||
@@ -4370,7 +4555,7 @@ namespace Benchmark
|
||||
Bus::ExecuteQueuedEvents();
|
||||
}
|
||||
s_benchmarkEBusEnv<Bus>.Disconnect(state);
|
||||
|
||||
|
||||
}
|
||||
BUS_BENCHMARK_REGISTER_ALL(BM_EBus_ExecuteBroadcast);
|
||||
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* 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/Settings/SettingsRegistryImpl.h>
|
||||
#include <AzCore/Settings/SettingsRegistryVisitorUtils.h>
|
||||
#include <AzCore/std/containers/fixed_vector.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
|
||||
namespace SettingsRegistryVisitorUtilsTests
|
||||
{
|
||||
struct VisitCallbackParams
|
||||
{
|
||||
AZStd::string_view m_inputJsonDocument;
|
||||
using VisitFieldFunction = bool(*)(AZ::SettingsRegistryInterface&,
|
||||
const AZ::SettingsRegistryVisitorUtils::VisitorCallback&,
|
||||
AZStd::string_view);
|
||||
|
||||
static inline constexpr size_t MaxFieldCount = 10;
|
||||
using ObjectFields = AZStd::fixed_vector<AZStd::pair<AZStd::string_view, AZStd::string_view>, MaxFieldCount>;
|
||||
using ArrayFields = AZStd::fixed_vector<AZStd::string_view, MaxFieldCount>;
|
||||
ObjectFields m_objectFields;
|
||||
ArrayFields m_arrayFields;
|
||||
};
|
||||
|
||||
template <typename VisitorParams>
|
||||
class SettingsRegistryVisitorUtilsParamFixture
|
||||
: public UnitTest::ScopedAllocatorSetupFixture
|
||||
, public ::testing::WithParamInterface<VisitorParams>
|
||||
{
|
||||
public:
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
m_registry = AZStd::make_unique<AZ::SettingsRegistryImpl>();
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
m_registry.reset();
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<AZ::SettingsRegistryImpl> m_registry;
|
||||
};
|
||||
|
||||
using SettingsRegistryVisitCallbackFixture = SettingsRegistryVisitorUtilsParamFixture<VisitCallbackParams>;
|
||||
|
||||
TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitFieldsOfArrayType_ReturnsFields)
|
||||
{
|
||||
const VisitCallbackParams& visitParams = GetParam();
|
||||
|
||||
ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
|
||||
|
||||
AZStd::fixed_vector<AZStd::string, VisitCallbackParams::MaxFieldCount> testArrayFields;
|
||||
auto visitorCallback = [this, &testArrayFields](AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type)
|
||||
{
|
||||
AZStd::string fieldValue;
|
||||
EXPECT_TRUE(m_registry->Get(fieldValue, path));
|
||||
testArrayFields.emplace_back(AZStd::move(fieldValue));
|
||||
};
|
||||
|
||||
AZ::SettingsRegistryVisitorUtils::VisitField(*m_registry, visitorCallback, "/Test/Array");
|
||||
|
||||
const AZStd::fixed_vector<AZStd::string, VisitCallbackParams::MaxFieldCount> expectedFields{
|
||||
visitParams.m_arrayFields.begin(), visitParams.m_arrayFields.end() };
|
||||
EXPECT_THAT(testArrayFields, ::testing::ContainerEq(expectedFields));
|
||||
}
|
||||
|
||||
TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitFieldsOfObjectType_ReturnsFields)
|
||||
{
|
||||
const VisitCallbackParams& visitParams = GetParam();
|
||||
|
||||
ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
|
||||
|
||||
AZStd::fixed_vector<AZStd::pair<AZStd::string, AZStd::string>, VisitCallbackParams::MaxFieldCount> testObjectFields;
|
||||
auto visitorCallback = [this, &testObjectFields](AZStd::string_view path, AZStd::string_view fieldName, AZ::SettingsRegistryInterface::Type)
|
||||
{
|
||||
AZStd::string fieldValue;
|
||||
EXPECT_TRUE(m_registry->Get(fieldValue, path));
|
||||
testObjectFields.emplace_back(fieldName, AZStd::move(fieldValue));
|
||||
};
|
||||
|
||||
AZ::SettingsRegistryVisitorUtils::VisitField(*m_registry, visitorCallback, "/Test/Object");
|
||||
|
||||
const AZStd::fixed_vector<AZStd::pair<AZStd::string, AZStd::string>, VisitCallbackParams::MaxFieldCount> expectedFields{
|
||||
visitParams.m_objectFields.begin(), visitParams.m_objectFields.end() };
|
||||
EXPECT_THAT(testObjectFields, ::testing::ContainerEq(expectedFields));
|
||||
}
|
||||
|
||||
TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitArrayOfArrayType_ReturnsFields)
|
||||
{
|
||||
const VisitCallbackParams& visitParams = GetParam();
|
||||
|
||||
ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
|
||||
|
||||
AZStd::fixed_vector<AZStd::string, VisitCallbackParams::MaxFieldCount> testArrayFields;
|
||||
auto visitorCallback = [this, &testArrayFields](AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type)
|
||||
{
|
||||
AZStd::string fieldValue;
|
||||
EXPECT_TRUE(m_registry->Get(fieldValue, path));
|
||||
testArrayFields.emplace_back(AZStd::move(fieldValue));
|
||||
};
|
||||
|
||||
AZ::SettingsRegistryVisitorUtils::VisitArray(*m_registry, visitorCallback, "/Test/Array");
|
||||
|
||||
const AZStd::fixed_vector<AZStd::string, VisitCallbackParams::MaxFieldCount> expectedArrayFields{
|
||||
visitParams.m_arrayFields.begin(), visitParams.m_arrayFields.end() };
|
||||
EXPECT_THAT(testArrayFields, ::testing::ContainerEq(expectedArrayFields));
|
||||
}
|
||||
|
||||
TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitArrayOfObjectType_ReturnsEmpty)
|
||||
{
|
||||
const VisitCallbackParams& visitParams = GetParam();
|
||||
|
||||
ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
|
||||
|
||||
AZStd::fixed_vector<AZStd::string, VisitCallbackParams::MaxFieldCount> testArrayFields;
|
||||
auto visitorCallback = [this, &testArrayFields](AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type)
|
||||
{
|
||||
AZStd::string fieldValue;
|
||||
EXPECT_TRUE(m_registry->Get(fieldValue, path));
|
||||
testArrayFields.emplace_back(AZStd::move(fieldValue));
|
||||
};
|
||||
|
||||
AZ::SettingsRegistryVisitorUtils::VisitArray(*m_registry, visitorCallback, "/Test/Object");
|
||||
|
||||
EXPECT_TRUE(testArrayFields.empty());
|
||||
}
|
||||
|
||||
TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitObjectOfArrayType_ReturnsEmpty)
|
||||
{
|
||||
const VisitCallbackParams& visitParams = GetParam();
|
||||
|
||||
ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
|
||||
|
||||
AZStd::fixed_vector<AZStd::pair<AZStd::string, AZStd::string>, VisitCallbackParams::MaxFieldCount> testObjectFields;
|
||||
auto visitorCallback = [this, &testObjectFields](AZStd::string_view path, AZStd::string_view fieldName, AZ::SettingsRegistryInterface::Type)
|
||||
{
|
||||
AZStd::string fieldValue;
|
||||
EXPECT_TRUE(m_registry->Get(fieldValue, path));
|
||||
testObjectFields.emplace_back(fieldName, AZStd::move(fieldValue));
|
||||
};
|
||||
|
||||
AZ::SettingsRegistryVisitorUtils::VisitObject(*m_registry, visitorCallback, "/Test/Array");
|
||||
|
||||
EXPECT_TRUE(testObjectFields.empty());
|
||||
}
|
||||
|
||||
TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitObjectOfObjectType_ReturnsFields)
|
||||
{
|
||||
const VisitCallbackParams& visitParams = GetParam();
|
||||
|
||||
ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
|
||||
|
||||
AZStd::fixed_vector<AZStd::pair<AZStd::string, AZStd::string>, VisitCallbackParams::MaxFieldCount> testObjectFields;
|
||||
auto visitorCallback = [this, &testObjectFields](AZStd::string_view path, AZStd::string_view fieldName, AZ::SettingsRegistryInterface::Type)
|
||||
{
|
||||
AZStd::string fieldValue;
|
||||
EXPECT_TRUE(m_registry->Get(fieldValue, path));
|
||||
testObjectFields.emplace_back(fieldName, AZStd::move(fieldValue));
|
||||
};
|
||||
|
||||
AZ::SettingsRegistryVisitorUtils::VisitObject(*m_registry, visitorCallback, "/Test/Object");
|
||||
|
||||
const AZStd::fixed_vector<AZStd::pair<AZStd::string, AZStd::string>, VisitCallbackParams::MaxFieldCount> expectedObjectFields{
|
||||
visitParams.m_objectFields.begin(), visitParams.m_objectFields.end() };
|
||||
EXPECT_THAT(testObjectFields, ::testing::ContainerEq(expectedObjectFields));
|
||||
}
|
||||
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
VisitField,
|
||||
SettingsRegistryVisitCallbackFixture,
|
||||
::testing::Values(
|
||||
VisitCallbackParams
|
||||
{
|
||||
R"({)" "\n"
|
||||
R"( "Test":)" "\n"
|
||||
R"( {)" "\n"
|
||||
R"( "Array": [ "Hello", "World" ],)" "\n"
|
||||
R"( "Object": { "Foo": "Hello", "Bar": "World"})" "\n"
|
||||
R"( })" "\n"
|
||||
R"(})" "\n",
|
||||
VisitCallbackParams::ObjectFields{{"Foo", "Hello"}, {"Bar", "World"}},
|
||||
VisitCallbackParams::ArrayFields{"Hello", "World"}
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -75,11 +75,12 @@ set(FILES
|
||||
Name/NameJsonSerializerTests.cpp
|
||||
Name/NameTests.cpp
|
||||
RTTI/TypeSafeIntegralTests.cpp
|
||||
SettingsRegistryTests.cpp
|
||||
SettingsRegistryMergeUtilsTests.cpp
|
||||
Settings/CommandLineTests.cpp
|
||||
Settings/SettingsRegistryTests.cpp
|
||||
Settings/SettingsRegistryConsoleUtilsTests.cpp
|
||||
Settings/SettingsRegistryMergeUtilsTests.cpp
|
||||
Settings/SettingsRegistryScriptUtilsTests.cpp
|
||||
Settings/SettingsRegistryVisitorUtilsTests.cpp
|
||||
Streamer/BlockCacheTests.cpp
|
||||
Streamer/DedicatedCacheTests.cpp
|
||||
Streamer/FullDecompressorTests.cpp
|
||||
|
||||
@@ -6,10 +6,11 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzQtComponents/AzQtComponents_Traits_Platform.h>
|
||||
#include <AzQtComponents/Components/Widgets/FileDialog.h>
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QRegExp>
|
||||
#include <QRegularExpression>
|
||||
|
||||
namespace AzQtComponents
|
||||
{
|
||||
@@ -24,7 +25,12 @@ namespace AzQtComponents
|
||||
// Trigger Qt's save filename dialog
|
||||
// If filePath isn't empty, it means we are prompting again because the filename was invalid,
|
||||
// so pass it instead of the directory so the filename is pre-filled in for the user
|
||||
filePath = QFileDialog::getSaveFileName(parent, caption, (filePath.isEmpty()) ? dir : filePath, filter, selectedFilter, options);
|
||||
QString localSelectedFilter;
|
||||
filePath = QFileDialog::getSaveFileName(parent, caption, (filePath.isEmpty()) ? dir : filePath, filter, &localSelectedFilter, options);
|
||||
if (selectedFilter)
|
||||
{
|
||||
*selectedFilter = localSelectedFilter;
|
||||
}
|
||||
|
||||
if (!filePath.isEmpty())
|
||||
{
|
||||
@@ -32,15 +38,39 @@ namespace AzQtComponents
|
||||
QString fileName = fileInfo.fileName();
|
||||
|
||||
// Check if the filename has any invalid characters
|
||||
QRegExp validFileNameRegex("^[a-zA-Z0-9_\\-./]*$");
|
||||
shouldPromptAgain = !validFileNameRegex.exactMatch(fileName);
|
||||
QRegularExpression validFileNameRegex("^[a-zA-Z0-9_\\-./]*$");
|
||||
QRegularExpressionMatch validFileNameMatch = validFileNameRegex.match(fileName);
|
||||
|
||||
// If the filename had invalid characters, then show a warning message and then we will re-prompt the save filename dialog
|
||||
if (shouldPromptAgain)
|
||||
if (!validFileNameMatch.hasMatch())
|
||||
{
|
||||
QMessageBox::warning(parent, QObject::tr("Invalid filename"),
|
||||
QObject::tr("O3DE assets are restricted to alphanumeric characters, hyphens (-), underscores (_), and dots (.)\n\n%1").arg(fileName));
|
||||
shouldPromptAgain = true;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
shouldPromptAgain = false;
|
||||
}
|
||||
#if AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_APPLY_MISSING_EXTENSION
|
||||
// If a filter was selected, then make sure that the resulting filename ends with that extension. On systems that use the default QFileDialog,
|
||||
// the extension is not guaranteed to be set in the resulting filename
|
||||
if (FileDialog::ApplyMissingExtension(localSelectedFilter, filePath))
|
||||
{
|
||||
// If an extension had to be applied, then the file dialog did not handle the case of overwriting existing files.
|
||||
// We need to check that condition before we proceed
|
||||
QFileInfo updatedFilePath(filePath);
|
||||
|
||||
if (updatedFilePath.exists())
|
||||
{
|
||||
QMessageBox::StandardButton overwriteSelection = QMessageBox::question(parent,
|
||||
QObject::tr("File exists"),
|
||||
QObject::tr("%1 exists. Do you want to overwrite the existing file?").arg(updatedFilePath.fileName()));
|
||||
shouldPromptAgain = (overwriteSelection == QMessageBox::No);
|
||||
}
|
||||
}
|
||||
#endif // AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_APPLY_MISSING_EXTENSION
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -51,4 +81,56 @@ namespace AzQtComponents
|
||||
|
||||
return filePath;
|
||||
}
|
||||
|
||||
bool FileDialog::ApplyMissingExtension(const QString& selectedFilter, QString& filePath)
|
||||
{
|
||||
if (selectedFilter.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// According to the QT documentation for QFileDialog, the selected filter will come in the form
|
||||
// <Filter Name> (<filter pattern1> <filter pattern2> .. <filter patternN> )
|
||||
//
|
||||
// For example:
|
||||
// "Images (*.gif *.png *.jpg)"
|
||||
//
|
||||
// Extract the contents of the <filter pattern>(s) inside the parenthesis and split them based on a whitespace or comma
|
||||
const QRegularExpression filterContent(".*\\((?<filters>[^\\)]+)\\)");
|
||||
QRegularExpressionMatch filterContentMatch = filterContent.match(selectedFilter);
|
||||
if (!filterContentMatch.hasMatch())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
QString filterExtensionsString = filterContentMatch.captured("filters");
|
||||
QStringList filterExtensionsFull = filterExtensionsString.split(" ", Qt::SkipEmptyParts);
|
||||
if (filterExtensionsFull.length() <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there are multiple suffixes in the selected filter, then default to the first one if a suffix needs to be appended
|
||||
QString defaultSuffix = filterExtensionsFull[0].mid(1);
|
||||
|
||||
// Iterate through the filter patterns to see if the current filename matches
|
||||
QFileInfo fileInfo(filePath);
|
||||
bool extensionNeeded = true;
|
||||
for (const QString& filterExtensionFull : filterExtensionsFull)
|
||||
{
|
||||
QString wildcardExpression = QRegularExpression::wildcardToRegularExpression(filterExtensionFull);
|
||||
QRegularExpression filterPattern(wildcardExpression, AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_FILTER_CASE_SENSITIVITY);
|
||||
QRegularExpressionMatch filterPatternMatch = filterPattern.match(fileInfo.fileName());
|
||||
if (filterPatternMatch.hasMatch())
|
||||
{
|
||||
// The filename matches one of the filter patterns already, the extension does not need to be added to the filename
|
||||
extensionNeeded = false;
|
||||
}
|
||||
}
|
||||
if (extensionNeeded)
|
||||
{
|
||||
// If the current (if any) suffix does not match, automatically add the default suffix for the selected filter
|
||||
filePath.append(defaultSuffix);
|
||||
}
|
||||
return extensionNeeded;
|
||||
}
|
||||
} // namespace AzQtComponents
|
||||
|
||||
@@ -24,6 +24,12 @@ namespace AzQtComponents
|
||||
static QString GetSaveFileName(QWidget* parent = nullptr, const QString& caption = QString(),
|
||||
const QString& dir = QString(), const QString& filter = QString(),
|
||||
QString* selectedFilter = nullptr, QFileDialog::Options options = QFileDialog::Options());
|
||||
|
||||
//! Helper method that parses a selected filter from Qt's QFileDialog::getSaveFileName and applies the
|
||||
//! selected filter's extension to the filePath if it doesnt already have the extension. This is needed
|
||||
//! on platforms that do not have a default file dialog (These platforms uses Qt's custom file dialog which will
|
||||
//! not apply the filter's extension automatically on user entered filenames)
|
||||
static bool ApplyMissingExtension(const QString& selectedFilter, QString& filePath);
|
||||
};
|
||||
|
||||
} // namespace AzQtComponents
|
||||
|
||||
@@ -12,4 +12,6 @@ set(FILES
|
||||
../../Utilities/QtWindowUtilities_linux.cpp
|
||||
../../Utilities/ScreenGrabber_linux.cpp
|
||||
../../../Platform/Linux/AzQtComponents/Components/StyledDockWidget_Linux.cpp
|
||||
../../../Platform/Linux/AzQtComponents/AzQtComponents_Traits_Linux.h
|
||||
../../../Platform/Linux/AzQtComponents/AzQtComponents_Traits_Platform.h
|
||||
)
|
||||
|
||||
@@ -12,4 +12,6 @@ set(FILES
|
||||
../../Utilities/QtWindowUtilities_mac.mm
|
||||
../../Utilities/ScreenGrabber_mac.mm
|
||||
../../../Platform/Mac/AzQtComponents/Components/StyledDockWidget_Mac.cpp
|
||||
../../../Platform/Mac/AzQtComponents/AzQtComponents_Traits_Mac.h
|
||||
../../../Platform/Mac/AzQtComponents/AzQtComponents_Traits_Platform.h
|
||||
)
|
||||
|
||||
+2
@@ -17,4 +17,6 @@ set(FILES
|
||||
../../Components/TitleBarOverdrawScreenHandler_win.h
|
||||
../../Components/TitleBarOverdrawScreenHandler_win.cpp
|
||||
../../../Platform/Windows/AzQtComponents/Components/StyledDockWidget_Windows.cpp
|
||||
../../../Platform/Windows/AzQtComponents/AzQtComponents_Traits_Windows.h
|
||||
../../../Platform/Windows/AzQtComponents/AzQtComponents_Traits_Platform.h
|
||||
)
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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 <AzTest/AzTest.h>
|
||||
#include <AzQtComponents/Components/Widgets/FileDialog.h>
|
||||
#include <QString>
|
||||
|
||||
TEST(AzQtComponents, ApplyMissingExtension_UpdateMissingExtension_Success)
|
||||
{
|
||||
const QString textFiler{"Text Files (*.txt)"};
|
||||
QString testPath{"testFile"};
|
||||
bool result = AzQtComponents::FileDialog::ApplyMissingExtension(textFiler, testPath);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_STRCASEEQ("testFile.txt", testPath.toUtf8().constData());
|
||||
}
|
||||
|
||||
TEST(AzQtComponents, ApplyMissingExtension_NoUpdateExistingExtension_Success)
|
||||
{
|
||||
const QString textFiler{"Text Files (*.txt)"};
|
||||
QString testPath{"testFile.txt"};
|
||||
bool result = AzQtComponents::FileDialog::ApplyMissingExtension(textFiler, testPath);
|
||||
EXPECT_FALSE(result);
|
||||
EXPECT_STRCASEEQ("testFile.txt", testPath.toUtf8().constData());
|
||||
}
|
||||
|
||||
TEST(AzQtComponents, ApplyMissingExtension_UpdateMissingExtensionMultipleExtensionFilter_Success)
|
||||
{
|
||||
const QString textFiler{"Image Files (*.jpg *.bmp *.png)"};
|
||||
QString testPath{"testFile"};
|
||||
bool result = AzQtComponents::FileDialog::ApplyMissingExtension(textFiler, testPath);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_STRCASEEQ("testFile.jpg", testPath.toUtf8().constData());
|
||||
}
|
||||
|
||||
TEST(AzQtComponents, ApplyMissingExtension_NoUpdateMissingExtensionMultipleExtensionFilter_Success)
|
||||
{
|
||||
const QString textFiler{"Image Files (*.jpg *.bmp *.png)"};
|
||||
QString testPath{"testFile.png"};
|
||||
bool result = AzQtComponents::FileDialog::ApplyMissingExtension(textFiler, testPath);
|
||||
EXPECT_FALSE(result);
|
||||
EXPECT_STRCASEEQ("testFile.png", testPath.toUtf8().constData());
|
||||
}
|
||||
|
||||
TEST(AzQtComponents, ApplyMissingExtension_NoUpdateMissingExtensionEmptyFilter_Success)
|
||||
{
|
||||
const QString textFiler{""};
|
||||
QString testPath{"testFile"};
|
||||
bool result = AzQtComponents::FileDialog::ApplyMissingExtension(textFiler, testPath);
|
||||
EXPECT_FALSE(result);
|
||||
EXPECT_STRCASEEQ("testFile", testPath.toUtf8().constData());
|
||||
}
|
||||
|
||||
TEST(AzQtComponents, ApplyMissingExtension_NoUpdateMissingExtensionInvalidFilter_Success)
|
||||
{
|
||||
const QString textFiler{"Bad Filter!!"};
|
||||
QString testPath{"testFile"};
|
||||
bool result = AzQtComponents::FileDialog::ApplyMissingExtension(textFiler, testPath);
|
||||
EXPECT_FALSE(result);
|
||||
EXPECT_STRCASEEQ("testFile", testPath.toUtf8().constData());
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
set(FILES
|
||||
Tests/AzQtComponentTests.cpp
|
||||
Tests/ColorControllerTests.cpp
|
||||
Tests/FileDialogTests.cpp
|
||||
Tests/FloatToStringConversionTests.cpp
|
||||
Tests/HexParsingTests.cpp
|
||||
Tests/StyleSheetCacheTests.cpp
|
||||
|
||||
@@ -10,6 +10,8 @@ if(NOT PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
return()
|
||||
endif()
|
||||
|
||||
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME})
|
||||
|
||||
ly_add_target(
|
||||
NAME AzQtComponents SHARED
|
||||
NAMESPACE AZ
|
||||
@@ -26,6 +28,7 @@ ly_add_target(
|
||||
AzQtComponents
|
||||
PUBLIC
|
||||
.
|
||||
${pal_dir}
|
||||
COMPILE_DEFINITIONS
|
||||
PRIVATE
|
||||
AZ_QT_COMPONENTS_EXPORT_SYMBOLS
|
||||
@@ -53,6 +56,7 @@ ly_add_target(
|
||||
.
|
||||
AzQtComponents
|
||||
AzQtComponents/Gallery
|
||||
${pal_dir}
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
3rdParty::Qt::Svg
|
||||
@@ -86,6 +90,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
PRIVATE
|
||||
Tests
|
||||
AzQtComponents
|
||||
${pal_dir}
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
AZ::AzQtComponents
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#define AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_APPLY_MISSING_EXTENSION 1
|
||||
#define AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_FILTER_CASE_SENSITIVITY QRegularExpression::NoPatternOption
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* 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 <AzQtComponents/AzQtComponents_Traits_Linux.h>
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#define AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_APPLY_MISSING_EXTENSION 0
|
||||
#define AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_FILTER_CASE_SENSITIVITY QRegularExpression::NoPatternOption
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* 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 <AzQtComponents/AzQtComponents_Traits_Mac.h>
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* 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 <AzQtComponents/AzQtComponents_Traits_Windows.h>
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#define AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_APPLY_MISSING_EXTENSION 0
|
||||
#define AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_FILTER_CASE_SENSITIVITY QRegularExpression::CaseInsensitiveOption
|
||||
@@ -410,7 +410,7 @@ namespace AzToolsFramework
|
||||
filter.append(ext);
|
||||
if (i < n - 1)
|
||||
{
|
||||
filter.append(", ");
|
||||
filter.append(" ");
|
||||
}
|
||||
}
|
||||
filter.append(")");
|
||||
|
||||
@@ -224,6 +224,7 @@ namespace AzToolsFramework
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ::Data::SerializedAssetTracker* assetTracker = settings.m_metadata.Find<AZ::Data::SerializedAssetTracker>();
|
||||
|
||||
referencedAssets = AZStd::move(assetTracker->GetTrackedAssets());
|
||||
@@ -245,6 +246,30 @@ namespace AzToolsFramework
|
||||
entityIdMapper.SetEntityIdGenerationApproach(InstanceEntityIdMapper::EntityIdGenerationApproach::Random);
|
||||
}
|
||||
|
||||
// some assets may come in from the JSON serialzier with no AssetID, but have an asset hint
|
||||
// this attempts to fix up the assets using the assetHint field
|
||||
auto fixUpInvalidAssets = [](AZ::Data::Asset<AZ::Data::AssetData>& asset)
|
||||
{
|
||||
if (!asset.GetId().IsValid() && !asset.GetHint().empty())
|
||||
{
|
||||
AZ::Data::AssetId assetId;
|
||||
AZ::Data::AssetCatalogRequestBus::BroadcastResult(
|
||||
assetId,
|
||||
&AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath,
|
||||
asset.GetHint().c_str(),
|
||||
AZ::Data::s_invalidAssetType,
|
||||
false);
|
||||
|
||||
if (assetId.IsValid())
|
||||
{
|
||||
asset.Create(assetId, true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
auto tracker = AZ::Data::SerializedAssetTracker{};
|
||||
tracker.SetAssetFixUp(fixUpInvalidAssets);
|
||||
|
||||
AZ::JsonDeserializerSettings settings;
|
||||
// The InstanceEntityIdMapper is registered twice because it's used in several places during deserialization where one is
|
||||
// specific for the InstanceEntityIdMapper and once for the generic JsonEntityIdMapper. Because the Json Serializer's meta
|
||||
@@ -252,16 +277,17 @@ namespace AzToolsFramework
|
||||
settings.m_metadata.Add(static_cast<AZ::JsonEntityIdSerializer::JsonEntityIdMapper*>(&entityIdMapper));
|
||||
settings.m_metadata.Add(&entityIdMapper);
|
||||
settings.m_metadata.Create<InstanceEntityScrubber>(newlyAddedEntities);
|
||||
settings.m_metadata.Add(tracker);
|
||||
|
||||
AZStd::string scratchBuffer;
|
||||
auto issueReportingCallback = [&scratchBuffer](
|
||||
AZStd::string_view message, AZ::JsonSerializationResult::ResultCode result,
|
||||
AZStd::string_view path) -> AZ::JsonSerializationResult::ResultCode
|
||||
AZStd::string_view message, AZ::JsonSerializationResult::ResultCode result,
|
||||
AZStd::string_view path) -> AZ::JsonSerializationResult::ResultCode
|
||||
{
|
||||
return Internal::JsonIssueReporter(scratchBuffer, message, result, path);
|
||||
};
|
||||
settings.m_reporting = AZStd::move(issueReportingCallback);
|
||||
|
||||
|
||||
AZ::JsonSerializationResult::ResultCode result = AZ::JsonSerialization::Load(instance, prefabDom, settings);
|
||||
|
||||
AZ::Data::AssetManager::Instance().ResumeAssetRelease();
|
||||
|
||||
+1
-1
@@ -1025,7 +1025,7 @@ namespace AzToolsFramework
|
||||
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/LuaScript.svg")
|
||||
->Attribute(AZ::Edit::Attributes::PrimaryAssetType, AZ::AzTypeInfo<AZ::ScriptAsset>::Uuid())
|
||||
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Script.png")
|
||||
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/lua-script/")
|
||||
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/scripting/lua-script/")
|
||||
->DataElement("AssetRef", &ScriptEditorComponent::m_scriptAsset, "Script", "Which script to use")
|
||||
->Attribute(AZ::Edit::Attributes::ChangeNotify, &ScriptEditorComponent::ScriptHasChanged)
|
||||
->Attribute("BrowseIcon", ":/stylesheet/img/UI20/browse-edit-select-files.svg")
|
||||
|
||||
@@ -22,22 +22,33 @@
|
||||
#include <LyShine/ILyShine.h>
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/Settings/SettingsRegistryVisitorUtils.h>
|
||||
#include <AzCore/StringFunc/StringFunc.h>
|
||||
|
||||
#include <AzCore/Script/ScriptSystemBus.h>
|
||||
|
||||
namespace LegacyLevelSystem
|
||||
{
|
||||
constexpr AZStd::string_view DeferredLoadLevelKey = "/O3DE/Runtime/SpawnableLevelSystem/DeferredLoadLevel";
|
||||
//------------------------------------------------------------------------
|
||||
static void LoadLevel(const AZ::ConsoleCommandContainer& arguments)
|
||||
{
|
||||
AZ_Error("SpawnableLevelSystem", !arguments.empty(), "LoadLevel requires a level file name to be provided.");
|
||||
AZ_Error("SpawnableLevelSystem", arguments.size() == 1, "LoadLevel requires a single level file name to be provided.");
|
||||
|
||||
if (!arguments.empty() && gEnv->pSystem && gEnv->pSystem->GetILevelSystem() && !gEnv->IsEditor())
|
||||
if (!arguments.empty() && gEnv && gEnv->pSystem && gEnv->pSystem->GetILevelSystem() && !gEnv->IsEditor())
|
||||
{
|
||||
gEnv->pSystem->GetILevelSystem()->LoadLevel(arguments[0].data());
|
||||
}
|
||||
else if (!arguments.empty())
|
||||
{
|
||||
// The SpawnableLevelSystem isn't available yet.
|
||||
// Defer the level load until later by storing it in the SettingsRegistry
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
settingsRegistry->Set(DeferredLoadLevelKey, arguments.front());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
@@ -45,7 +56,7 @@ namespace LegacyLevelSystem
|
||||
{
|
||||
AZ_Warning("SpawnableLevelSystem", !arguments.empty(), "UnloadLevel doesn't use any arguments.");
|
||||
|
||||
if (gEnv->pSystem && gEnv->pSystem->GetILevelSystem() && !gEnv->IsEditor())
|
||||
if (gEnv && gEnv->pSystem && gEnv->pSystem->GetILevelSystem() && !gEnv->IsEditor())
|
||||
{
|
||||
gEnv->pSystem->GetILevelSystem()->UnloadLevel();
|
||||
}
|
||||
@@ -73,6 +84,24 @@ namespace LegacyLevelSystem
|
||||
}
|
||||
|
||||
AzFramework::RootSpawnableNotificationBus::Handler::BusConnect();
|
||||
|
||||
// If there were LoadLevel command invocations before the creation of the level system
|
||||
// then those invocations were queued.
|
||||
// load the last level in the queue, since only one level can be loaded at a time
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
if (AZ::SettingsRegistryInterface::FixedValueString deferredLevelName;
|
||||
settingsRegistry->Get(deferredLevelName, DeferredLoadLevelKey) && !deferredLevelName.empty())
|
||||
{
|
||||
// since this is the constructor any derived classes vtables aren't setup yet
|
||||
// call this class LoadLevel function
|
||||
AZ_TracePrintf("SpawnableLevelSystem", "The Level System is now available."
|
||||
" Loading level %s which could not be loaded earlier\n", deferredLevelName.c_str());
|
||||
SpawnableLevelSystem::LoadLevel(deferredLevelName.c_str());
|
||||
// Delete the key with the deferred level name
|
||||
settingsRegistry->Remove(DeferredLoadLevelKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
@@ -173,7 +202,7 @@ namespace LegacyLevelSystem
|
||||
}
|
||||
|
||||
// Make sure a spawnable level exists that matches levelname
|
||||
AZStd::string validLevelName = "";
|
||||
AZStd::string validLevelName;
|
||||
AZ::Data::AssetId rootSpawnableAssetId;
|
||||
AZ::Data::AssetCatalogRequestBus::BroadcastResult(
|
||||
rootSpawnableAssetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, levelName, nullptr, false);
|
||||
|
||||
@@ -395,7 +395,7 @@ namespace LUAEditor
|
||||
|
||||
void LUAEditorMainWindow::OnLuaDocumentation()
|
||||
{
|
||||
QDesktopServices::openUrl(QUrl("http://docs.aws.amazon.com/lumberyard/latest/developerguide/lua-scripting-intro.html"));
|
||||
QDesktopServices::openUrl(QUrl("https://o3de.org/docs/user-guide/scripting/lua/"));
|
||||
}
|
||||
|
||||
void LUAEditorMainWindow::OnMenuCloseCurrentWindow()
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace AWSCore
|
||||
/**
|
||||
* Add required SystemComponents to the SystemEntity.
|
||||
*/
|
||||
virtual AZ::ComponentTypeList GetRequiredSystemComponents() const override;
|
||||
AZ::ComponentTypeList GetRequiredSystemComponents() const override;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace AWSCore
|
||||
|
||||
|
||||
AWSCoreConfiguration();
|
||||
~AWSCoreConfiguration() = default;
|
||||
~AWSCoreConfiguration() override = default;
|
||||
|
||||
void ActivateConfig();
|
||||
void DeactivateConfig();
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace AWSCore
|
||||
{
|
||||
public:
|
||||
AWSDefaultCredentialHandler();
|
||||
~AWSDefaultCredentialHandler() = default;
|
||||
~AWSDefaultCredentialHandler() override = default;
|
||||
|
||||
//! Activate handler and its credentials provider, make sure activation
|
||||
//! invoked after AWSNativeSDK init to avoid memory leak
|
||||
|
||||
+3
-4
@@ -15,13 +15,12 @@
|
||||
namespace AWSCore
|
||||
{
|
||||
//! Defines AWSCoreAttributionConsent QT dialog as QT message box.
|
||||
class AWSCoreAttributionConsentDialog :
|
||||
public QMessageBox
|
||||
class AWSCoreAttributionConsentDialog
|
||||
: public QMessageBox
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(AWSCoreAttributionConsentDialog, AZ::SystemAllocator, 0);
|
||||
AWSCoreAttributionConsentDialog();
|
||||
virtual ~AWSCoreAttributionConsentDialog() = default;
|
||||
|
||||
~AWSCoreAttributionConsentDialog() override = default;
|
||||
};
|
||||
} // namespace AWSCore
|
||||
|
||||
@@ -18,4 +18,4 @@ namespace AWSCore
|
||||
static constexpr char AwsAttributionAttributeKeyActiveAWSGems[] = "aws_gems";
|
||||
static constexpr char AwsAttributionAttributeKeyTimestamp[] = "timestamp";
|
||||
|
||||
} // namespace AWSCOre
|
||||
} // namespace AWSCore
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace AWSCore
|
||||
"Failed to launch Resource Mapping Tool, please check <a href=\"file:///%s\">logs</a> for details.";
|
||||
|
||||
AWSCoreEditorMenu(const QString& text);
|
||||
~AWSCoreEditorMenu();
|
||||
~AWSCoreEditorMenu() override;
|
||||
|
||||
private:
|
||||
QAction* AddExternalLinkAction(const AZStd::string& name, const AZStd::string& url, const AZStd::string& icon = "");
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace AWSCore
|
||||
};
|
||||
|
||||
AWSResourceMappingManager();
|
||||
~AWSResourceMappingManager() = default;
|
||||
~AWSResourceMappingManager() override = default;
|
||||
|
||||
void ActivateManager();
|
||||
void DeactivateManager();
|
||||
|
||||
@@ -64,9 +64,6 @@ namespace AWSCore
|
||||
|
||||
/// Initialize an AwsApiClientJobConfig object.
|
||||
///
|
||||
/// \param DefaultConfigType - the type of the config object from which
|
||||
/// default values will be taken.
|
||||
///
|
||||
/// \param defaultConfig - the config object that provides values when
|
||||
/// no override has been set in this object. The default is nullptr, which
|
||||
/// will cause a default value to be used.
|
||||
@@ -83,10 +80,10 @@ namespace AWSCore
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~AwsApiClientJobConfig() = default;
|
||||
~AwsApiClientJobConfig() override = default;
|
||||
|
||||
/// Gets a client initialized used currently applied settings. If
|
||||
/// any settings change after first use, code must call
|
||||
/// any settings change after first use, code must call
|
||||
/// ApplySettings before those changes will take effect.
|
||||
std::shared_ptr<ClientType> GetClient() override
|
||||
{
|
||||
@@ -112,7 +109,7 @@ namespace AWSCore
|
||||
}
|
||||
else
|
||||
{
|
||||
// If no explict credenitals are provided then AWS C++ SDK will perform standard search
|
||||
// If no explicit credentials are provided then AWS C++ SDK will perform standard search
|
||||
return std::make_shared<ClientType>(Aws::Auth::AWSCredentials(), GetClientConfiguration());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,14 +14,12 @@
|
||||
|
||||
namespace AWSCore
|
||||
{
|
||||
|
||||
/// Base class for all AWS jobs. Primarily exists so that
|
||||
/// Base class for all AWS jobs. Primarily exists so that
|
||||
/// AwsApiJob::s_config can be used for settings that apply to
|
||||
/// all AWS jobs.
|
||||
class AwsApiJob
|
||||
: public AZ::Job
|
||||
{
|
||||
|
||||
public:
|
||||
// To use a different allocator, extend this class and use this macro.
|
||||
AZ_CLASS_ALLOCATOR(AwsApiJob, AZ::SystemAllocator, 0);
|
||||
@@ -33,11 +31,10 @@ namespace AWSCore
|
||||
|
||||
protected:
|
||||
AwsApiJob(bool isAutoDelete, IConfig* config = GetDefaultConfig());
|
||||
virtual ~AwsApiJob();
|
||||
~AwsApiJob() override = default;
|
||||
|
||||
/// Used for error messages.
|
||||
static const char* COMPONENT_DISPLAY_NAME;
|
||||
|
||||
};
|
||||
|
||||
} // namespace AWSCore
|
||||
|
||||
@@ -96,9 +96,6 @@ namespace AWSCore
|
||||
|
||||
/// Initialize an AwsApiClientJobConfig object.
|
||||
///
|
||||
/// \param DefaultConfigType - the type of the config object from which
|
||||
/// default values will be taken.
|
||||
///
|
||||
/// \param defaultConfig - the config object that provides values when
|
||||
/// no override has been set in this object. The default is nullptr, which
|
||||
/// will cause a default value to be used.
|
||||
@@ -146,7 +143,7 @@ namespace AWSCore
|
||||
#endif
|
||||
Override<Aws::String> caFile;
|
||||
|
||||
/// Applys settings changes made after first use.
|
||||
/// Applies settings changes made after first use.
|
||||
virtual void ApplySettings();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -217,7 +214,7 @@ namespace AWSCore
|
||||
: protected AWSCoreNotificationsBus::Handler
|
||||
{
|
||||
public:
|
||||
~AwsApiJobConfigHolder()
|
||||
~AwsApiJobConfigHolder() override
|
||||
{
|
||||
AWSCoreNotificationsBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
@@ -145,10 +145,10 @@ namespace AWSCore
|
||||
AWS_API_REQUEST_TRAITS_TEMPLATE_DEFINITION_HELPER
|
||||
typename AWS_API_REQUEST_TRAITS_TEMPLATE_INSTANCE_HELPER::AsyncFunctionType AWS_API_REQUEST_TRAITS_TEMPLATE_INSTANCE_HELPER::AsyncFunction = _AsyncFunction;
|
||||
|
||||
/// Macro that simplifies the declaration of an AwsRequstJob that has a result.
|
||||
/// Macro that simplifies the declaration of an AwsRequestJob that has a result.
|
||||
#define AWS_API_REQUEST_JOB(SERVICE, REQUEST) AWSCore::AwsApiRequestJob<AWS_API_REQUEST_TRAITS(SERVICE, REQUEST)>
|
||||
|
||||
/// Macro that simplifies the declaration of an AwsRequstJob that has no result.
|
||||
/// Macro that simplifies the declaration of an AwsRequestJob that has no result.
|
||||
#define AWS_API_REQUEST_JOB_NO_RESULT(SERVICE, REQUEST) AWSCore::AwsApiRequestJob<AWS_API_REQUEST_TRAITS_NO_RESULT(SERVICE, REQUEST)>
|
||||
|
||||
/// An Az::Job that that executes a specific AWS request.
|
||||
@@ -257,7 +257,7 @@ namespace AWSCore
|
||||
/// of request data until your running on the job's worker thread,
|
||||
/// instead of setting the request data before calling Start.
|
||||
///
|
||||
/// \param true if the request should be made.
|
||||
/// \return true if the request should be made.
|
||||
virtual bool PrepareRequest()
|
||||
{
|
||||
return true;
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace AWSCore
|
||||
: public AZ::ComponentBus
|
||||
{
|
||||
public:
|
||||
virtual ~HttpClientComponentNotifications() {}
|
||||
~HttpClientComponentNotifications() override = default;
|
||||
virtual void OnHttpRequestSuccess(int responseCode, AZStd::string responseBody) {}
|
||||
virtual void OnHttpRequestFailure(int responseCode) {}
|
||||
};
|
||||
@@ -55,7 +55,7 @@ namespace AWSCore
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(HttpClientComponent, "{23ECDBDF-129A-4670-B9B4-1E0B541ACD61}");
|
||||
virtual ~HttpClientComponent() = default;
|
||||
~HttpClientComponent() override = default;
|
||||
|
||||
void Init() override;
|
||||
void Activate() override;
|
||||
|
||||
@@ -178,7 +178,7 @@ namespace AWSCore
|
||||
};
|
||||
|
||||
/// Override to process the response to the HTTP request before callbacks are fired.
|
||||
/// WARNING: This gets called on the job's thread, so observe thread safety precations.
|
||||
/// WARNING: This gets called on the job's thread, so observe thread safety precautions.
|
||||
virtual void ProcessResponse(const std::shared_ptr<Aws::Http::HttpResponse>& response)
|
||||
{
|
||||
AZ_UNUSED(response);
|
||||
|
||||
@@ -29,24 +29,24 @@ namespace AWSCore
|
||||
Ch Peek() const
|
||||
{
|
||||
int c = m_is.peek();
|
||||
return c == std::char_traits<char>::eof() ? '\0' : (Ch)c;
|
||||
return c == std::char_traits<char>::eof() ? '\0' : static_cast<Ch>(c);
|
||||
}
|
||||
|
||||
Ch Take()
|
||||
{
|
||||
int c = m_is.get();
|
||||
return c == std::char_traits<char>::eof() ? '\0' : (Ch)c;
|
||||
return c == std::char_traits<char>::eof() ? '\0' : static_cast<Ch>(c);
|
||||
}
|
||||
|
||||
size_t Tell() const
|
||||
{
|
||||
return (size_t)m_is.tellg();
|
||||
return static_cast<size_t>(m_is.tellg());
|
||||
}
|
||||
|
||||
Ch* PutBegin()
|
||||
{
|
||||
AZ_Assert(false, "Not Implemented");
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Put(Ch)
|
||||
|
||||
@@ -161,7 +161,7 @@ namespace AWSCore
|
||||
}
|
||||
|
||||
/// Write JSON format content directly to the writer's output stream.
|
||||
/// This can be used to efficently output static content.
|
||||
/// This can be used to efficiently output static content.
|
||||
bool WriteJson(const Ch* json)
|
||||
{
|
||||
if (json)
|
||||
@@ -182,7 +182,7 @@ namespace AWSCore
|
||||
}
|
||||
|
||||
/// Write an object. The object can implement a WriteJson function
|
||||
/// or you can provide an GobalWriteJson template function
|
||||
/// or you can provide an GlobalWriteJson template function
|
||||
/// specialization.
|
||||
template<class ObjectType>
|
||||
bool Object(const ObjectType& obj)
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace AWSCore
|
||||
class RequestBuilder
|
||||
{
|
||||
public:
|
||||
RequestBuilder() = default;
|
||||
RequestBuilder();
|
||||
|
||||
/// Converts the provided object to JSON and sends it as the
|
||||
/// body of the request. The object can implement the following
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace AWSCore
|
||||
{
|
||||
|
||||
public:
|
||||
virtual const AZStd::string GetServiceUrl() = 0;
|
||||
virtual AZStd::string GetServiceUrl() = 0;
|
||||
};
|
||||
|
||||
/// Encapsulates what code needs to know about a service in order to
|
||||
@@ -81,9 +81,6 @@ namespace AWSCore
|
||||
|
||||
/// Initialize an ServiceClientJobConfig object.
|
||||
///
|
||||
/// \param DefaultConfigType - the type of the config object from which
|
||||
/// default values will be taken.
|
||||
///
|
||||
/// \param defaultConfig - the config object that provides values when
|
||||
/// no override has been set in this object. The default is nullptr, which
|
||||
/// will cause a default value to be used.
|
||||
@@ -102,7 +99,7 @@ namespace AWSCore
|
||||
|
||||
/// This implementation assumes the caller will cache this value as
|
||||
/// needed. See it's use in ServiceRequestJobConfig.
|
||||
const AZStd::string GetServiceUrl() override
|
||||
AZStd::string GetServiceUrl() override
|
||||
{
|
||||
if (endpointOverride.has_value())
|
||||
{
|
||||
|
||||
@@ -119,7 +119,7 @@ namespace AWSCore
|
||||
Error error;
|
||||
|
||||
/// Determines if the AWS credentials, as supplied by the credentialsProvider from
|
||||
/// the ServiceReqestJobConfig object (which defaults to the user's credentials),
|
||||
/// the ServiceRequestJobConfig object (which defaults to the user's credentials),
|
||||
/// are used to sign the request. The default is true. Override this and return false
|
||||
/// if calling a public API and want to avoid the overhead of signing requests.
|
||||
bool UseAWSCredentials() {
|
||||
@@ -565,13 +565,11 @@ namespace AWSCore
|
||||
}
|
||||
|
||||
AZStd::string requestContent;
|
||||
AZStd::string responseContent;
|
||||
|
||||
std::istreambuf_iterator<AZStd::string::value_type> eos;
|
||||
|
||||
std::shared_ptr<Aws::IOStream> requestStream = response->GetOriginatingRequest().GetContentBody();
|
||||
if (requestStream)
|
||||
{
|
||||
std::istreambuf_iterator<AZStd::string::value_type> eos;
|
||||
requestStream->clear();
|
||||
requestStream->seekg(0);
|
||||
requestContent = AZStd::string{ std::istreambuf_iterator<AZStd::string::value_type>(*requestStream.get()),eos };
|
||||
@@ -584,7 +582,7 @@ namespace AWSCore
|
||||
Aws::IOStream& responseStream = response->GetResponseBody();
|
||||
responseStream.clear();
|
||||
responseStream.seekg(0);
|
||||
responseContent = AZStd::string{ std::istreambuf_iterator<AZStd::string::value_type>(responseStream),responseEos };
|
||||
AZStd::string responseContent = AZStd::string{ std::istreambuf_iterator<AZStd::string::value_type>(responseStream), responseEos };
|
||||
responseContent = EscapePercentCharsInString(responseContent);
|
||||
responseStream.seekg(0);
|
||||
|
||||
|
||||
@@ -44,9 +44,6 @@ namespace AWSCore
|
||||
|
||||
/// Initialize an ServiceRequestJobConfig object.
|
||||
///
|
||||
/// \param DefaultConfigType - the type of the config object from which
|
||||
/// default values will be taken.
|
||||
///
|
||||
/// \param defaultConfig - the config object that provides values when
|
||||
/// no override has been set in this object. The default is nullptr, which
|
||||
/// will cause a default value to be used.
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace AWSCore
|
||||
QMenuBar* menuBar = mainWindow->menuBar();
|
||||
QList<QAction*> actionList = menuBar->actions();
|
||||
QAction* insertPivot = nullptr;
|
||||
for (QList<QAction*>::iterator itr = actionList.begin(); itr != actionList.end(); itr++)
|
||||
for (QList<QAction*>::iterator itr = actionList.begin(); itr != actionList.end(); ++itr)
|
||||
{
|
||||
if (QString::compare((*itr)->text(), EDITOR_HELP_MENU_TEXT) == 0)
|
||||
{
|
||||
@@ -88,7 +88,7 @@ namespace AWSCore
|
||||
}
|
||||
}
|
||||
|
||||
auto menu = m_awsCoreEditorManager->GetAWSCoreEditorMenu();
|
||||
const auto menu = m_awsCoreEditorManager->GetAWSCoreEditorMenu();
|
||||
if (insertPivot)
|
||||
{
|
||||
menuBar->insertMenu(insertPivot, menu);
|
||||
|
||||
@@ -35,8 +35,7 @@ namespace AWSCore
|
||||
this->setDefaultButton(QMessageBox::Save);
|
||||
this->button(QMessageBox::Cancel)->hide();
|
||||
this->setIcon(QMessageBox::Information);
|
||||
QGridLayout* layout = (QGridLayout*)this->layout();
|
||||
if (layout)
|
||||
if (QGridLayout* layout = static_cast<QGridLayout*>(this->layout()))
|
||||
{
|
||||
layout->setVerticalSpacing(20);
|
||||
layout->setHorizontalSpacing(10);
|
||||
|
||||
@@ -68,19 +68,19 @@ namespace AWSCore
|
||||
AZ_Assert(fileIO, "File IO is not initialized.");
|
||||
|
||||
// Resolve path to editor_aws_preferences.setreg
|
||||
AZStd::string editorAWSPreferencesFilePath =
|
||||
const AZStd::string editorAWSPreferencesFilePath =
|
||||
AZStd::string::format("@user@/%s/%s", AZ::SettingsRegistryInterface::RegistryFolder, EditorAWSPreferencesFileName);
|
||||
AZStd::array<char, AZ::IO::MaxPathLength> resolvedPathAWSPreference{};
|
||||
if (!fileIO->ResolvePath(editorAWSPreferencesFilePath.c_str(), resolvedPathAWSPreference.data(), resolvedPathAWSPreference.size()))
|
||||
AZ::IO::FixedMaxPath resolvedPathAWSPreference;
|
||||
if (!fileIO->ResolvePath(resolvedPathAWSPreference, AZ::IO::PathView(editorAWSPreferencesFilePath)))
|
||||
{
|
||||
AZ_Warning("AWSAttributionManager", false, "Error resolving path %s", resolvedPathAWSPreference.data());
|
||||
AZ_Warning("AWSAttributionManager", false, "Error resolving path %s", resolvedPathAWSPreference.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (fileIO->Exists(resolvedPathAWSPreference.data()))
|
||||
if (fileIO->Exists(resolvedPathAWSPreference.c_str()))
|
||||
{
|
||||
m_settingsRegistry->MergeSettingsFile(
|
||||
resolvedPathAWSPreference.data(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, "");
|
||||
resolvedPathAWSPreference.String(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, "");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,8 +136,8 @@ namespace AWSCore
|
||||
return true;
|
||||
}
|
||||
|
||||
AZStd::chrono::seconds lastSendTimeStamp = AZStd::chrono::seconds(lastSendTimeStampSeconds);
|
||||
AZStd::chrono::seconds secondsSinceLastSend =
|
||||
const AZStd::chrono::seconds lastSendTimeStamp = AZStd::chrono::seconds(lastSendTimeStampSeconds);
|
||||
const AZStd::chrono::seconds secondsSinceLastSend =
|
||||
AZStd::chrono::duration_cast<AZStd::chrono::seconds>(AZStd::chrono::system_clock::now().time_since_epoch()) - lastSendTimeStamp;
|
||||
if (static_cast<AZ::u64>(secondsSinceLastSend.count()) >= delayInSeconds)
|
||||
{
|
||||
@@ -154,7 +154,7 @@ namespace AWSCore
|
||||
if (credentialResult.result)
|
||||
{
|
||||
std::shared_ptr<Aws::Auth::AWSCredentialsProvider> provider = credentialResult.result;
|
||||
auto creds = provider->GetAWSCredentials();
|
||||
const auto creds = provider->GetAWSCredentials();
|
||||
if (!creds.IsEmpty())
|
||||
{
|
||||
return true;
|
||||
@@ -200,9 +200,13 @@ namespace AWSCore
|
||||
AZ_Assert(fileIO, "File IO is not initialized.");
|
||||
|
||||
// Resolve path to editor_aws_preferences.setreg
|
||||
AZStd::string editorPreferencesFilePath = AZStd::string::format("@user@/%s/%s", AZ::SettingsRegistryInterface::RegistryFolder, EditorAWSPreferencesFileName);
|
||||
AZStd::array<char, AZ::IO::MaxPathLength> resolvedPath {};
|
||||
fileIO->ResolvePath(editorPreferencesFilePath.c_str(), resolvedPath.data(), resolvedPath.size());
|
||||
const AZStd::string editorPreferencesFilePath = AZStd::string::format("@user@/%s/%s", AZ::SettingsRegistryInterface::RegistryFolder, EditorAWSPreferencesFileName);
|
||||
AZ::IO::FixedMaxPath resolvedPathAWSPreference;
|
||||
if (!fileIO->ResolvePath(resolvedPathAWSPreference, AZ::IO::PathView(editorPreferencesFilePath)))
|
||||
{
|
||||
AZ_Warning("AWSAttributionManager", false, "Error resolving path %s", editorPreferencesFilePath.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::SettingsRegistryMergeUtils::DumperSettings dumperSettings;
|
||||
dumperSettings.m_prettifyOutput = true;
|
||||
@@ -215,14 +219,14 @@ namespace AWSCore
|
||||
{
|
||||
AZ_Warning(
|
||||
"AWSAttributionManager", false, R"(Unable to save changes to the Editor AWS Preferences registry file at "%s"\n)",
|
||||
resolvedPath.data());
|
||||
resolvedPathAWSPreference.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
bool saved {};
|
||||
constexpr auto configurationMode =
|
||||
AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY;
|
||||
if (AZ::IO::SystemFile outputFile; outputFile.Open(resolvedPath.data(), configurationMode))
|
||||
if (AZ::IO::SystemFile outputFile; outputFile.Open(resolvedPathAWSPreference.c_str(), configurationMode))
|
||||
{
|
||||
saved = outputFile.Write(stringBuffer.data(), stringBuffer.size()) == stringBuffer.size();
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace AWSCore
|
||||
{
|
||||
if (m_resourceMappingToolWatcher->IsProcessRunning())
|
||||
{
|
||||
m_resourceMappingToolWatcher->TerminateProcess(AZ::u32(-1));
|
||||
m_resourceMappingToolWatcher->TerminateProcess(static_cast<AZ::u32>(-1));
|
||||
}
|
||||
m_resourceMappingToolWatcher.reset();
|
||||
}
|
||||
@@ -214,7 +214,7 @@ namespace AWSCore
|
||||
QMenu* AWSCoreEditorMenu::SetAWSFeatureSubMenu(const AZStd::string& menuText)
|
||||
{
|
||||
auto actionList = this->actions();
|
||||
for (QList<QAction*>::iterator itr = actionList.begin(); itr != actionList.end(); itr++)
|
||||
for (QList<QAction*>::iterator itr = actionList.begin(); itr != actionList.end(); ++itr)
|
||||
{
|
||||
if (QString::compare((*itr)->text(), menuText.c_str()) == 0)
|
||||
{
|
||||
|
||||
@@ -22,10 +22,6 @@ namespace AWSCore
|
||||
{
|
||||
}
|
||||
|
||||
AwsApiJob::~AwsApiJob()
|
||||
{
|
||||
}
|
||||
|
||||
AwsApiJob::Config* AwsApiJob::GetDefaultConfig()
|
||||
{
|
||||
static AwsApiJobConfigHolder<AwsApiJob::Config> s_configHolder{};
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace AWSCore
|
||||
{
|
||||
m_fileFields.emplace_back(FileField{ std::move(fieldName), std::move(fileName) , AZStd::vector<char>{} });
|
||||
m_fileFields.back().m_fileData.reserve(length);
|
||||
m_fileFields.back().m_fileData.assign((const char*)bytes, (const char*)bytes + length);
|
||||
m_fileFields.back().m_fileData.assign(static_cast<const char*>(bytes), static_cast<const char*>(bytes) + length);
|
||||
}
|
||||
|
||||
void MultipartFormData::SetCustomBoundary(AZStd::string boundary)
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
|
||||
namespace AWSCore
|
||||
{
|
||||
RequestBuilder::RequestBuilder()
|
||||
: m_httpMethod(Aws::Http::HttpMethod::HTTP_GET)
|
||||
{
|
||||
}
|
||||
|
||||
bool RequestBuilder::SetPathParameterUnescaped(const char* key, const char* value)
|
||||
{
|
||||
|
||||
@@ -26,7 +26,6 @@ namespace AWSCore
|
||||
: m_status(Status::NotLoaded)
|
||||
, m_defaultAccountId("")
|
||||
, m_defaultRegion("")
|
||||
, m_resourceMappings()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -164,7 +163,7 @@ namespace AWSCore
|
||||
m_defaultRegion = jsonDocument.FindMember(ResourceMappingRegionKeyName)->value.GetString();
|
||||
|
||||
auto resourceMappings = jsonDocument.FindMember(ResourceMappingResourcesKeyName)->value.GetObject();
|
||||
for (auto mappingIter = resourceMappings.MemberBegin(); mappingIter != resourceMappings.MemberEnd(); mappingIter++)
|
||||
for (auto mappingIter = resourceMappings.MemberBegin(); mappingIter != resourceMappings.MemberEnd(); ++mappingIter)
|
||||
{
|
||||
auto mappingValue = mappingIter->value.GetObject();
|
||||
if (mappingValue.MemberCount() != 0)
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace AWSCore
|
||||
[](DynamoDBGetItemRequestJob* job) // OnSuccess handler
|
||||
{
|
||||
auto item = job->result.GetItem();
|
||||
if (item.size() > 0)
|
||||
if (!item.empty())
|
||||
{
|
||||
DynamoDBAttributeValueMap result;
|
||||
for (const auto& itermPair : item)
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
AWSCoreNotificationsBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
~AWSCoreNotificationsBusMock()
|
||||
~AWSCoreNotificationsBusMock() override
|
||||
{
|
||||
AWSCoreNotificationsBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ class AWSCVarCredentialHandlerTest
|
||||
{
|
||||
public:
|
||||
AWSCVarCredentialHandlerTest() = default;
|
||||
virtual ~AWSCVarCredentialHandlerTest() = default;
|
||||
~AWSCVarCredentialHandlerTest() override = default;
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
|
||||
@@ -36,14 +36,14 @@ public:
|
||||
m_credentialsProvider.reset();
|
||||
}
|
||||
|
||||
int GetCredentialHandlerOrder() const
|
||||
int GetCredentialHandlerOrder() const override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::shared_ptr<Aws::Auth::AWSCredentialsProvider> GetCredentialsProvider()
|
||||
std::shared_ptr<Aws::Auth::AWSCredentialsProvider> GetCredentialsProvider() override
|
||||
{
|
||||
m_handlerCounter++;
|
||||
++m_handlerCounter;
|
||||
return m_credentialsProvider;
|
||||
}
|
||||
|
||||
@@ -72,14 +72,14 @@ public:
|
||||
m_credentialsProvider.reset();
|
||||
}
|
||||
|
||||
int GetCredentialHandlerOrder() const
|
||||
int GetCredentialHandlerOrder() const override
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
std::shared_ptr<Aws::Auth::AWSCredentialsProvider> GetCredentialsProvider()
|
||||
std::shared_ptr<Aws::Auth::AWSCredentialsProvider> GetCredentialsProvider() override
|
||||
{
|
||||
m_handlerCounter++;
|
||||
++m_handlerCounter;
|
||||
return m_credentialsProvider;
|
||||
}
|
||||
|
||||
@@ -115,10 +115,10 @@ public:
|
||||
|
||||
TEST_F(AWSCredentialBusTest, GetCredentialsProvider_CallFromMultithread_GetExpectedCredentialsProviderAndNumberOfCalls)
|
||||
{
|
||||
int testThreadNumber = 10;
|
||||
constexpr int testThreadNumber = 10;
|
||||
AZStd::atomic<int> actualEbusCalls = 0;
|
||||
AZStd::vector<AZStd::thread> testThreadPool;
|
||||
for (int index = 0; index < testThreadNumber; index++)
|
||||
for (int index = 0; index < testThreadNumber; ++index)
|
||||
{
|
||||
testThreadPool.emplace_back(AZStd::thread([&]() {
|
||||
AWSCredentialResult result;
|
||||
|
||||
@@ -49,7 +49,7 @@ class AWSDefaultCredentialHandlerTest
|
||||
{
|
||||
public:
|
||||
AWSDefaultCredentialHandlerTest() = default;
|
||||
virtual ~AWSDefaultCredentialHandlerTest() = default;
|
||||
~AWSDefaultCredentialHandlerTest() override = default;
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
|
||||
@@ -23,6 +23,11 @@ class AWSApiClientJobConfigTest
|
||||
, public AWSCredentialRequestBus::Handler
|
||||
{
|
||||
public:
|
||||
AWSApiClientJobConfigTest()
|
||||
: m_credentialHandlerCounter(0)
|
||||
{
|
||||
}
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
AWSNativeSDKInit::InitializationManager::InitAwsApi();
|
||||
|
||||
@@ -84,7 +84,7 @@ class ServiceClientJobConfigTest
|
||||
void ReloadConfigFile(bool reloadConfigFileName = false) override
|
||||
{
|
||||
AZ_UNUSED(reloadConfigFileName);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(ServiceClientJobConfigTest, GetServiceUrl_CreateServiceWithServiceNameOnly_GetExpectedFeatureServiceUrl)
|
||||
|
||||
@@ -217,7 +217,7 @@ TEST_F(AWSResourceMappingManagerTest, ActivateManager_ParseValidConfigFile_Confi
|
||||
CreateTestConfigFile(TEST_VALID_RESOURCE_MAPPING_CONFIG_FILE);
|
||||
m_resourceMappingManager->ActivateManager();
|
||||
|
||||
int testThreadNumber = 10;
|
||||
constexpr int testThreadNumber = 10;
|
||||
AZStd::atomic<int> actualEbusCalls = 0;
|
||||
AZStd::vector<AZStd::thread> testThreadPool;
|
||||
for (int index = 0; index < testThreadNumber; index++)
|
||||
@@ -226,7 +226,7 @@ TEST_F(AWSResourceMappingManagerTest, ActivateManager_ParseValidConfigFile_Confi
|
||||
AZStd::string actualAccountId;
|
||||
AWSResourceMappingRequestBus::BroadcastResult(actualAccountId, &AWSResourceMappingRequests::GetDefaultAccountId);
|
||||
EXPECT_FALSE(actualAccountId.empty());
|
||||
actualEbusCalls++;
|
||||
++actualEbusCalls;
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -44,19 +44,19 @@ TEST_F(AWSResourceMappingUtilsTest, FormatRESTApiUrl_PassingInvalidRESTApiId_Ret
|
||||
{
|
||||
auto actualUrl = AWSResourceMappingUtils::FormatRESTApiUrl("", TEST_VALID_RESTAPI_REGION, TEST_VALID_RESTAPI_STAGE);
|
||||
|
||||
EXPECT_TRUE(actualUrl == "");
|
||||
EXPECT_TRUE(actualUrl.empty());
|
||||
}
|
||||
|
||||
TEST_F(AWSResourceMappingUtilsTest, FormatRESTApiUrl_PassingInvalidRESTApiRegion_ReturnEmptyResult)
|
||||
{
|
||||
auto actualUrl = AWSResourceMappingUtils::FormatRESTApiUrl(TEST_VALID_RESTAPI_ID, "", TEST_VALID_RESTAPI_STAGE);
|
||||
|
||||
EXPECT_TRUE(actualUrl == "");
|
||||
EXPECT_TRUE(actualUrl.empty());
|
||||
}
|
||||
|
||||
TEST_F(AWSResourceMappingUtilsTest, FormatRESTApiUrl_PassingInvalidRESTApiStage_ReturnEmptyResult)
|
||||
{
|
||||
auto actualUrl = AWSResourceMappingUtils::FormatRESTApiUrl(TEST_VALID_RESTAPI_ID, TEST_VALID_RESTAPI_REGION, "");
|
||||
|
||||
EXPECT_TRUE(actualUrl == "");
|
||||
EXPECT_TRUE(actualUrl.empty());
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ public:
|
||||
AWSScriptBehaviorDynamoDBNotificationBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
~AWSScriptBehaviorDynamoDBNotificationBusHandlerMock()
|
||||
~AWSScriptBehaviorDynamoDBNotificationBusHandlerMock() override
|
||||
{
|
||||
AWSScriptBehaviorDynamoDBNotificationBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ public:
|
||||
AWSScriptBehaviorLambdaNotificationBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
~AWSScriptBehaviorLambdaNotificationBusHandlerMock()
|
||||
~AWSScriptBehaviorLambdaNotificationBusHandlerMock() override
|
||||
{
|
||||
AWSScriptBehaviorLambdaNotificationBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ public:
|
||||
AWSScriptBehaviorS3NotificationBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
~AWSScriptBehaviorS3NotificationBusHandlerMock()
|
||||
~AWSScriptBehaviorS3NotificationBusHandlerMock() override
|
||||
{
|
||||
AWSScriptBehaviorS3NotificationBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
@@ -107,8 +107,8 @@ class AWSCoreFixture
|
||||
: public UnitTest::ScopedAllocatorSetupFixture
|
||||
{
|
||||
public:
|
||||
AWSCoreFixture() {}
|
||||
virtual ~AWSCoreFixture() = default;
|
||||
AWSCoreFixture() = default;
|
||||
~AWSCoreFixture() override = default;
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
|
||||
@@ -64,6 +64,16 @@ To add additional dependencies, for example other CDK libraries, just add
|
||||
them to your `setup.py` file and rerun the `pip install -r requirements.txt`
|
||||
command.
|
||||
|
||||
## Optional Features
|
||||
Server access logging is enabled by default. To disable the feature, use the following commands to synthesize and deploy this CDK application.
|
||||
|
||||
```
|
||||
$ cdk synth -c disable_access_log=true --all
|
||||
$ cdk deploy -c disable_access_log=true --all
|
||||
```
|
||||
|
||||
See https://docs.aws.amazon.com/AmazonS3/latest/userguide/ServerLogs.html for more information about server access logging.
|
||||
|
||||
## Useful commands
|
||||
|
||||
* `cdk ls` list all stacks in the app
|
||||
|
||||
@@ -57,8 +57,9 @@ example_stack = ExampleResources(
|
||||
tags={Constants.O3DE_PROJECT_TAG_NAME: PROJECT_NAME, Constants.O3DE_FEATURE_TAG_NAME: FEATURE_NAME},
|
||||
env=env
|
||||
)
|
||||
#
|
||||
# Add the common stack as a dependency of the feature stack
|
||||
|
||||
# Add the core stack as a dependency of the feature stack since the feature stack
|
||||
# requires the core stack outputs for deployment.
|
||||
example_stack.add_dependency(core_construct.common_stack)
|
||||
|
||||
app.synth()
|
||||
|
||||
@@ -60,17 +60,6 @@ class CoreStack(core.Stack):
|
||||
type='TAG_FILTERS_1_0')
|
||||
)
|
||||
|
||||
# Create an S3 bucket for Amazon S3 server access logging
|
||||
# See https://docs.aws.amazon.com/AmazonS3/latest/dev/security-best-practices.html
|
||||
self._server_access_logs_bucket = s3.Bucket(
|
||||
self,
|
||||
f'{self._project_name}-{self._feature_name}-Access-Log-Bucket',
|
||||
block_public_access=s3.BlockPublicAccess.BLOCK_ALL,
|
||||
encryption=s3.BucketEncryption.S3_MANAGED,
|
||||
access_control=s3.BucketAccessControl.LOG_DELIVERY_WRITE
|
||||
)
|
||||
self._server_access_logs_bucket.grant_read(self._admin_group)
|
||||
|
||||
# Define exports
|
||||
# Export resource group
|
||||
self._resource_group_output = core.CfnOutput(
|
||||
@@ -94,10 +83,22 @@ class CoreStack(core.Stack):
|
||||
export_name=f"{self._project_name}:AdminGroup",
|
||||
value=self._admin_group.group_arn)
|
||||
|
||||
# Export access log bucket name
|
||||
self._server_access_logs_bucket_output = core.CfnOutput(
|
||||
self,
|
||||
id=f'ServerAccessLogsBucketOutput',
|
||||
description='Name of the S3 bucket for storing server access logs generated by the sample CDK application(s)',
|
||||
export_name=f"{self._project_name}:ServerAccessLogsBucket",
|
||||
value=self._server_access_logs_bucket.bucket_name)
|
||||
# Create an S3 bucket for Amazon S3 server access logging
|
||||
# See https://docs.aws.amazon.com/AmazonS3/latest/dev/security-best-practices.html
|
||||
if self.node.try_get_context('disable_access_log') != 'true':
|
||||
self._server_access_logs_bucket = s3.Bucket(
|
||||
self,
|
||||
f'{self._project_name}-{self._feature_name}-Access-Log-Bucket',
|
||||
block_public_access=s3.BlockPublicAccess.BLOCK_ALL,
|
||||
encryption=s3.BucketEncryption.S3_MANAGED,
|
||||
access_control=s3.BucketAccessControl.LOG_DELIVERY_WRITE
|
||||
)
|
||||
self._server_access_logs_bucket.grant_read(self._admin_group)
|
||||
|
||||
# Export access log bucket name
|
||||
self._server_access_logs_bucket_output = core.CfnOutput(
|
||||
self,
|
||||
id=f'ServerAccessLogsBucketOutput',
|
||||
description='Name of the S3 bucket for storing server access logs generated by the sample CDK application(s)',
|
||||
export_name=f"{self._project_name}:ServerAccessLogsBucket",
|
||||
value=self._server_access_logs_bucket.bucket_name)
|
||||
|
||||
@@ -118,19 +118,23 @@ class ExampleResources(core.Stack):
|
||||
# https://docs.aws.amazon.com/AmazonS3/latest/userguide/serv-side-encryption.html
|
||||
# 3. Enable Amazon S3 server access logging
|
||||
# https://docs.aws.amazon.com/AmazonS3/latest/userguide/ServerLogs.html
|
||||
server_access_logs_bucket = s3.Bucket.from_bucket_name(
|
||||
self,
|
||||
f'{self._project_name}-{self._feature_name}-ImportedAccessLogsBucket',
|
||||
core.Fn.import_value(f"{self._project_name}:ServerAccessLogsBucket")
|
||||
)
|
||||
server_access_logs_bucket = None
|
||||
if self.node.try_get_context('disable_access_log') != 'true':
|
||||
server_access_logs_bucket = s3.Bucket.from_bucket_name(
|
||||
self,
|
||||
f'{self._project_name}-{self._feature_name}-ImportedAccessLogsBucket',
|
||||
core.Fn.import_value(f"{self._project_name}:ServerAccessLogsBucket")
|
||||
)
|
||||
|
||||
example_bucket = s3.Bucket(
|
||||
self,
|
||||
f'{self._project_name}-{self._feature_name}-Example-S3bucket',
|
||||
block_public_access=s3.BlockPublicAccess.BLOCK_ALL,
|
||||
encryption=s3.BucketEncryption.S3_MANAGED,
|
||||
server_access_logs_bucket=server_access_logs_bucket,
|
||||
server_access_logs_prefix=f'{self._project_name}-{self._feature_name}-{self.region}-AccessLogs'
|
||||
server_access_logs_bucket=
|
||||
server_access_logs_bucket if server_access_logs_bucket else None,
|
||||
server_access_logs_prefix=
|
||||
f'{self._project_name}-{self._feature_name}-{self.region}-AccessLogs' if server_access_logs_bucket else None
|
||||
)
|
||||
|
||||
s3_deployment.BucketDeployment(
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ namespace AZ
|
||||
}
|
||||
|
||||
//! Returns the buffer asset that is used for all skinned mesh outputs
|
||||
virtual Data::Asset<RPI::BufferAsset> GetBufferAsset() const = 0;
|
||||
virtual Data::Asset<RPI::BufferAsset> GetBufferAsset() = 0;
|
||||
|
||||
//! Returns the buffer that is used for all skinned mesh outputs
|
||||
virtual Data::Instance<RPI::Buffer> GetBuffer() = 0;
|
||||
|
||||
+31
-4
@@ -8,6 +8,8 @@
|
||||
|
||||
#include <SkinnedMesh/SkinnedMeshOutputStreamManager.h>
|
||||
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
|
||||
#include <Atom/Feature/SkinnedMesh/SkinnedMeshVertexStreams.h>
|
||||
#include <Atom/Feature/SkinnedMesh/SkinnedMeshFeatureProcessorBus.h>
|
||||
|
||||
@@ -72,11 +74,32 @@ namespace AZ
|
||||
creator.End(m_bufferAsset);
|
||||
}
|
||||
|
||||
|
||||
// default value of 256mb supports roughly 42 character instances at 100,000 vertices per character x 64 bytes per vertex (12 byte position + 12 byte previous frame position + 12 byte normal + 16 byte tangent + 12 byte bitangent)
|
||||
// This includes only the output of the skinning compute shader, not the input buffers or bone transforms
|
||||
AZ_CVAR(
|
||||
int,
|
||||
r_skinnedMeshInstanceMemoryPoolSize,
|
||||
256,
|
||||
nullptr,
|
||||
AZ::ConsoleFunctorFlags::NeedsReload,
|
||||
"The amount of memory in Mb available for all actor skinning data. Note that this must only be set once at application startup"
|
||||
);
|
||||
|
||||
void SkinnedMeshOutputStreamManager::Init()
|
||||
{
|
||||
// 256mb supports roughly 42 character instances at 100,000 vertices per character x 64 bytes per vertex (12 byte position + 12 byte previous frame position + 12 byte normal + 16 byte tangent + 12 byte bitangent)
|
||||
// This includes only the output of the skinning compute shader, not the input buffers or bone transforms
|
||||
m_sizeInBytes = 256u * (1024u * 1024u);
|
||||
}
|
||||
|
||||
void SkinnedMeshOutputStreamManager::EnsureInit()
|
||||
{
|
||||
if (!m_needsInit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_needsInit = false;
|
||||
|
||||
const AZ::u64 sizeInMb = r_skinnedMeshInstanceMemoryPoolSize;
|
||||
m_sizeInBytes = sizeInMb * (1024u * 1024u);
|
||||
|
||||
CalculateAlignment();
|
||||
|
||||
@@ -90,6 +113,8 @@ namespace AZ
|
||||
RHI::VirtualAddress result;
|
||||
{
|
||||
AZStd::lock_guard<AZStd::mutex> lock(m_allocatorMutex);
|
||||
|
||||
EnsureInit();
|
||||
result = m_freeListAllocator.Allocate(byteCount, m_alignment);
|
||||
}
|
||||
|
||||
@@ -127,13 +152,15 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
Data::Asset<RPI::BufferAsset> SkinnedMeshOutputStreamManager::GetBufferAsset() const
|
||||
Data::Asset<RPI::BufferAsset> SkinnedMeshOutputStreamManager::GetBufferAsset()
|
||||
{
|
||||
EnsureInit();
|
||||
return m_bufferAsset;
|
||||
}
|
||||
|
||||
Data::Instance<RPI::Buffer> SkinnedMeshOutputStreamManager::GetBuffer()
|
||||
{
|
||||
EnsureInit();
|
||||
if (!m_buffer)
|
||||
{
|
||||
m_buffer = RPI::Buffer::FindOrCreate(m_bufferAsset);
|
||||
|
||||
@@ -39,13 +39,14 @@ namespace AZ
|
||||
AZStd::intrusive_ptr<SkinnedMeshOutputStreamAllocation> Allocate(size_t byteCount) override;
|
||||
void DeAllocate(RHI::VirtualAddress allocation) override;
|
||||
void DeAllocateNoSignal(RHI::VirtualAddress allocation) override;
|
||||
Data::Asset<RPI::BufferAsset> GetBufferAsset() const override;
|
||||
Data::Asset<RPI::BufferAsset> GetBufferAsset() override;
|
||||
Data::Instance<RPI::Buffer> GetBuffer() override;
|
||||
|
||||
private:
|
||||
// SystemTickBus
|
||||
void OnSystemTick() override;
|
||||
|
||||
void EnsureInit();
|
||||
void GarbageCollect();
|
||||
void CalculateAlignment();
|
||||
void CreateBufferAsset();
|
||||
@@ -58,6 +59,7 @@ namespace AZ
|
||||
size_t m_sizeInBytes = 0;
|
||||
bool m_memoryWasFreed = false;
|
||||
bool m_broadcastMemoryAvailableEvent = false;
|
||||
bool m_needsInit = true;
|
||||
};
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
|
||||
+1
-1
@@ -71,7 +71,7 @@ namespace AZ
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->Attribute(
|
||||
AZ::Edit::Attributes::HelpPageURL,
|
||||
"https://o3de.org/docs/user-guide/components/reference/attachment/")
|
||||
"https://o3de.org/docs/user-guide/components/reference/animation/attachment/")
|
||||
->DataElement(0, &EditorAttachmentComponent::m_targetId, "Target entity", "Attach to this entity.")
|
||||
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorAttachmentComponent::OnTargetIdChanged)
|
||||
->DataElement(
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@ namespace AZ
|
||||
->Attribute(Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/AreaLight.svg")
|
||||
->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
|
||||
->Attribute(Edit::Attributes::AutoExpand, true)
|
||||
->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/area-light/")
|
||||
->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/light/")
|
||||
;
|
||||
|
||||
editContext->Class<AreaLightComponentController>(
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@ namespace AZ
|
||||
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") // [GFX TODO][ATOM-1998] create icons.
|
||||
->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
|
||||
->Attribute(Edit::Attributes::AutoExpand, true)
|
||||
->Attribute(Edit::Attributes::HelpPageURL, "https://") // [GFX TODO][ATOM-1998] create page
|
||||
->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/directional-light/") // [GFX TODO][ATOM-1998] create page
|
||||
;
|
||||
|
||||
editContext->Class<DirectionalLightComponentController>(
|
||||
|
||||
+1
@@ -54,6 +54,7 @@ namespace AZ
|
||||
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg")
|
||||
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/diffuse-probe-grid/")
|
||||
->Attribute(AZ::Edit::Attributes::PrimaryAssetType, AZ::AzTypeInfo<RPI::ModelAsset>::Uuid())
|
||||
->ClassElement(AZ::Edit::ClassElements::Group, "Probe Spacing")
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace AZ
|
||||
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg")
|
||||
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/")
|
||||
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/grid/")
|
||||
;
|
||||
|
||||
editContext->Class<GridComponentController>(
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ namespace AZ
|
||||
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg")
|
||||
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/")
|
||||
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/global-skylight-ibl/")
|
||||
;
|
||||
|
||||
editContext->Class<ImageBasedLightComponentController>(
|
||||
|
||||
+1
-1
@@ -86,7 +86,7 @@ namespace AZ
|
||||
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg")
|
||||
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/")
|
||||
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/material/")
|
||||
->Attribute(AZ::Edit::Attributes::PrimaryAssetType, AZ::AzTypeInfo<RPI::MaterialAsset>::Uuid())
|
||||
->DataElement(AZ::Edit::UIHandlers::MultiLineEdit, &EditorMaterialComponent::m_message, "Message", "")
|
||||
->Attribute(AZ_CRC("PlaceholderText", 0xa23ec278), "Component cannot be edited with multiple entities selected")
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace AZ
|
||||
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Mesh.svg")
|
||||
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/")
|
||||
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/mesh/")
|
||||
->Attribute(AZ::Edit::Attributes::PrimaryAssetType, AZ::AzTypeInfo<RPI::ModelAsset>::Uuid())
|
||||
->DataElement(AZ::Edit::UIHandlers::Button, &EditorMeshComponent::m_addMaterialComponentFlag, "Add Material Component", "Add Material Component")
|
||||
->Attribute(AZ::Edit::Attributes::NameLabelOverride, "")
|
||||
|
||||
+1
@@ -37,6 +37,7 @@ namespace AZ
|
||||
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg")
|
||||
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/occlusion-culling-plane/")
|
||||
;
|
||||
|
||||
editContext->Class<OcclusionCullingPlaneComponentController>(
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ namespace AZ
|
||||
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") // [GFX TODO ATOM-2672][PostFX] need to create icons for PostProcessing.
|
||||
->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
|
||||
->Attribute(Edit::Attributes::AutoExpand, true)
|
||||
->Attribute(Edit::Attributes::HelpPageURL, "https://") // [TODO ATOM-2672][PostFX] need create page for PostProcessing.
|
||||
->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/bloom/") // [TODO ATOM-2672][PostFX] need create page for PostProcessing.
|
||||
;
|
||||
|
||||
editContext->Class<BloomComponentController>(
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ namespace AZ
|
||||
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") // [GFX TODO ATOM-2672][PostFX] need to create icons for PostProcessing.y
|
||||
->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
|
||||
->Attribute(Edit::Attributes::AutoExpand, true)
|
||||
->Attribute(Edit::Attributes::HelpPageURL, "https://") // [GFX TODO][ATOM-2672][PostFX] need create page for PostProcessing.
|
||||
->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/depth-of-field/") // [GFX TODO][ATOM-2672][PostFX] need create page for PostProcessing.
|
||||
;
|
||||
|
||||
editContext->Class<DepthOfFieldComponentController>(
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ namespace AZ
|
||||
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") // [GFX TODO][ATOM-2672][PostFX] need to create icons for PostProcessing.
|
||||
->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZStd::vector<AZ::Crc32>({ AZ_CRC("Level", 0x9aeacc13), AZ_CRC("Game", 0x232b318c) }))
|
||||
->Attribute(Edit::Attributes::AutoExpand, true)
|
||||
->Attribute(Edit::Attributes::HelpPageURL, "https://") // [GFX TODO][ATOM-2672][PostFX] need to create page for PostProcessing.
|
||||
->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/display-mapper/") // [GFX TODO][ATOM-2672][PostFX] need to create page for PostProcessing.
|
||||
;
|
||||
|
||||
editContext->Class<DisplayMapperComponentController>(
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ namespace AZ
|
||||
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg")
|
||||
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/")
|
||||
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/postfx-layer/")
|
||||
;
|
||||
|
||||
editContext->Class<PostFxLayerComponentController>(
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ namespace AZ
|
||||
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") // [GFX TODO ATOM-2672][PostFX] need to create icons for PostProcessing.
|
||||
->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
|
||||
->Attribute(Edit::Attributes::AutoExpand, true)
|
||||
->Attribute(Edit::Attributes::HelpPageURL, "https://") // [TODO ATOM-2672][PostFX] need create page for PostProcessing.
|
||||
->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/exposure-control/") // [TODO ATOM-2672][PostFX] need create page for PostProcessing.
|
||||
;
|
||||
|
||||
editContext->Class<ExposureControlComponentController>(
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user