Merge branch 'development' into LYN-5265_state_tracker_impl
commit
1cdb34326b
@ -0,0 +1,162 @@
|
|||||||
|
#
|
||||||
|
# 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
|
||||||
|
#
|
||||||
|
#
|
||||||
|
import os, traceback, binascii, sys, json, pathlib
|
||||||
|
import azlmbr.math
|
||||||
|
import azlmbr.bus
|
||||||
|
|
||||||
|
#
|
||||||
|
# SceneAPI Processor
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
def log_exception_traceback():
|
||||||
|
exc_type, exc_value, exc_tb = sys.exc_info()
|
||||||
|
data = traceback.format_exception(exc_type, exc_value, exc_tb)
|
||||||
|
print(str(data))
|
||||||
|
|
||||||
|
def get_mesh_node_names(sceneGraph):
|
||||||
|
import azlmbr.scene as sceneApi
|
||||||
|
import azlmbr.scene.graph
|
||||||
|
from scene_api import scene_data as sceneData
|
||||||
|
|
||||||
|
meshDataList = []
|
||||||
|
node = sceneGraph.get_root()
|
||||||
|
children = []
|
||||||
|
paths = []
|
||||||
|
|
||||||
|
while node.IsValid():
|
||||||
|
# store children to process after siblings
|
||||||
|
if sceneGraph.has_node_child(node):
|
||||||
|
children.append(sceneGraph.get_node_child(node))
|
||||||
|
|
||||||
|
nodeName = sceneData.SceneGraphName(sceneGraph.get_node_name(node))
|
||||||
|
paths.append(nodeName.get_path())
|
||||||
|
|
||||||
|
# store any node that has mesh data content
|
||||||
|
nodeContent = sceneGraph.get_node_content(node)
|
||||||
|
if nodeContent.CastWithTypeName('MeshData'):
|
||||||
|
if sceneGraph.is_node_end_point(node) is False:
|
||||||
|
if (len(nodeName.get_path())):
|
||||||
|
meshDataList.append(sceneData.SceneGraphName(sceneGraph.get_node_name(node)))
|
||||||
|
|
||||||
|
# advance to next node
|
||||||
|
if sceneGraph.has_node_sibling(node):
|
||||||
|
node = sceneGraph.get_node_sibling(node)
|
||||||
|
elif children:
|
||||||
|
node = children.pop()
|
||||||
|
else:
|
||||||
|
node = azlmbr.scene.graph.NodeIndex()
|
||||||
|
|
||||||
|
return meshDataList, paths
|
||||||
|
|
||||||
|
def update_manifest(scene):
|
||||||
|
import json
|
||||||
|
import uuid, os
|
||||||
|
import azlmbr.scene as sceneApi
|
||||||
|
import azlmbr.scene.graph
|
||||||
|
from scene_api import scene_data as sceneData
|
||||||
|
|
||||||
|
graph = sceneData.SceneGraph(scene.graph)
|
||||||
|
# Get a list of all the mesh nodes, as well as all the nodes
|
||||||
|
mesh_name_list, all_node_paths = get_mesh_node_names(graph)
|
||||||
|
scene_manifest = sceneData.SceneManifest()
|
||||||
|
|
||||||
|
clean_filename = scene.sourceFilename.replace('.', '_')
|
||||||
|
|
||||||
|
# Compute the filename of the scene file
|
||||||
|
source_basepath = scene.watchFolder
|
||||||
|
source_relative_path = os.path.dirname(os.path.relpath(clean_filename, source_basepath))
|
||||||
|
source_filename_only = os.path.basename(clean_filename)
|
||||||
|
|
||||||
|
created_entities = []
|
||||||
|
|
||||||
|
# Loop every mesh node in the scene
|
||||||
|
for activeMeshIndex in range(len(mesh_name_list)):
|
||||||
|
mesh_name = mesh_name_list[activeMeshIndex]
|
||||||
|
mesh_path = mesh_name.get_path()
|
||||||
|
# Create a unique mesh group name using the filename + node name
|
||||||
|
mesh_group_name = '{}_{}'.format(source_filename_only, mesh_name.get_name())
|
||||||
|
# Remove forbidden filename characters from the name since this will become a file on disk later
|
||||||
|
mesh_group_name = "".join(char for char in mesh_group_name if char not in "|<>:\"/?*\\")
|
||||||
|
# Add the MeshGroup to the manifest and give it a unique ID
|
||||||
|
mesh_group = scene_manifest.add_mesh_group(mesh_group_name)
|
||||||
|
mesh_group['id'] = '{' + str(uuid.uuid5(uuid.NAMESPACE_DNS, source_filename_only + mesh_path)) + '}'
|
||||||
|
# Set our current node as the only node that is included in this MeshGroup
|
||||||
|
scene_manifest.mesh_group_select_node(mesh_group, mesh_path)
|
||||||
|
|
||||||
|
# Explicitly remove all other nodes to prevent implicit inclusions
|
||||||
|
for node in all_node_paths:
|
||||||
|
if node != mesh_path:
|
||||||
|
scene_manifest.mesh_group_unselect_node(mesh_group, node)
|
||||||
|
|
||||||
|
# Create an editor entity
|
||||||
|
entity_id = azlmbr.entity.EntityUtilityBus(azlmbr.bus.Broadcast, "CreateEditorReadyEntity", mesh_group_name)
|
||||||
|
# Add an EditorMeshComponent to the entity
|
||||||
|
editor_mesh_component = azlmbr.entity.EntityUtilityBus(azlmbr.bus.Broadcast, "GetOrAddComponentByTypeName", entity_id, "AZ::Render::EditorMeshComponent")
|
||||||
|
# Set the ModelAsset assetHint to the relative path of the input asset + the name of the MeshGroup we just created + the azmodel extension
|
||||||
|
# The MeshGroup we created will be output as a product in the asset's path named mesh_group_name.azmodel
|
||||||
|
# The assetHint will be converted to an AssetId later during prefab loading
|
||||||
|
json_update = json.dumps({
|
||||||
|
"Controller": { "Configuration": { "ModelAsset": {
|
||||||
|
"assetHint": os.path.join(source_relative_path, mesh_group_name) + ".azmodel" }}}
|
||||||
|
});
|
||||||
|
# Apply the JSON above to the component we created
|
||||||
|
result = azlmbr.entity.EntityUtilityBus(azlmbr.bus.Broadcast, "UpdateComponentForEntity", entity_id, editor_mesh_component, json_update)
|
||||||
|
|
||||||
|
if not result:
|
||||||
|
raise RuntimeError("UpdateComponentForEntity failed")
|
||||||
|
|
||||||
|
# Keep track of the entity we set up, we'll add them all to the prefab we're creating later
|
||||||
|
created_entities.append(entity_id)
|
||||||
|
|
||||||
|
# Create a prefab with all our entities
|
||||||
|
prefab_filename = source_filename_only + ".prefab"
|
||||||
|
created_template_id = azlmbr.prefab.PrefabSystemScriptingBus(azlmbr.bus.Broadcast, "CreatePrefab", created_entities, prefab_filename)
|
||||||
|
|
||||||
|
if created_template_id == azlmbr.prefab.InvalidTemplateId:
|
||||||
|
raise RuntimeError("CreatePrefab {} failed".format(prefab_filename))
|
||||||
|
|
||||||
|
# Convert the prefab to a JSON string
|
||||||
|
output = azlmbr.prefab.PrefabLoaderScriptingBus(azlmbr.bus.Broadcast, "SaveTemplateToString", created_template_id)
|
||||||
|
|
||||||
|
if output.IsSuccess():
|
||||||
|
jsonString = output.GetValue()
|
||||||
|
uuid = azlmbr.math.Uuid_CreateRandom().ToString()
|
||||||
|
jsonResult = json.loads(jsonString)
|
||||||
|
# Add a PrefabGroup to the manifest and store the JSON on it
|
||||||
|
scene_manifest.add_prefab_group(source_filename_only, uuid, jsonResult)
|
||||||
|
else:
|
||||||
|
raise RuntimeError("SaveTemplateToString failed for template id {}, prefab {}".format(created_template_id, prefab_filename))
|
||||||
|
|
||||||
|
# Convert the manifest to a JSON string and return it
|
||||||
|
new_manifest = scene_manifest.export()
|
||||||
|
|
||||||
|
return new_manifest
|
||||||
|
|
||||||
|
sceneJobHandler = None
|
||||||
|
|
||||||
|
def on_update_manifest(args):
|
||||||
|
try:
|
||||||
|
scene = args[0]
|
||||||
|
return update_manifest(scene)
|
||||||
|
except RuntimeError as err:
|
||||||
|
print (f'ERROR - {err}')
|
||||||
|
log_exception_traceback()
|
||||||
|
|
||||||
|
global sceneJobHandler
|
||||||
|
sceneJobHandler = None
|
||||||
|
|
||||||
|
# try to create SceneAPI handler for processing
|
||||||
|
try:
|
||||||
|
import azlmbr.scene as sceneApi
|
||||||
|
if (sceneJobHandler == None):
|
||||||
|
sceneJobHandler = sceneApi.ScriptBuildingNotificationBusHandler()
|
||||||
|
sceneJobHandler.connect()
|
||||||
|
sceneJobHandler.add_callback('OnUpdateManifest', on_update_manifest)
|
||||||
|
except:
|
||||||
|
sceneJobHandler = None
|
||||||
@ -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)
|
||||||
@ -0,0 +1,194 @@
|
|||||||
|
"""
|
||||||
|
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")
|
||||||
|
reflection_probe_creation = (
|
||||||
|
"Reflection Probe Entity successfully created",
|
||||||
|
"Reflection Probe Entity failed to be created")
|
||||||
|
reflection_probe_component = (
|
||||||
|
"Entity has a Reflection Probe component",
|
||||||
|
"Entity failed to find Reflection Probe component")
|
||||||
|
reflection_probe_disabled = (
|
||||||
|
"Reflection Probe component disabled",
|
||||||
|
"Reflection Probe component was not disabled.")
|
||||||
|
reflection_map_generated = (
|
||||||
|
"Reflection Probe cubemap generated",
|
||||||
|
"Reflection Probe cubemap not generated")
|
||||||
|
box_shape_component = (
|
||||||
|
"Entity has a Box Shape component",
|
||||||
|
"Entity did not have a Box Shape component")
|
||||||
|
reflection_probe_enabled = (
|
||||||
|
"Reflection Probe component enabled",
|
||||||
|
"Reflection Probe component was not enabled.")
|
||||||
|
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_ReflectionProbe_AddedToEntity():
|
||||||
|
"""
|
||||||
|
Summary:
|
||||||
|
Tests the Reflection Probe 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 Reflection Probe entity with no components.
|
||||||
|
2) Add a Reflection Probe component to Reflection Probe entity.
|
||||||
|
3) UNDO the entity creation and component addition.
|
||||||
|
4) REDO the entity creation and component addition.
|
||||||
|
5) Verify Reflection Probe component not enabled.
|
||||||
|
6) Add Shape component since it is required by the Reflection Probe component.
|
||||||
|
7) Verify Reflection Probe component is enabled.
|
||||||
|
8) Enter/Exit game mode.
|
||||||
|
9) Test IsHidden.
|
||||||
|
10) Test IsVisible.
|
||||||
|
11) Verify cubemap generation
|
||||||
|
12) Delete Reflection Probe entity.
|
||||||
|
13) UNDO deletion.
|
||||||
|
14) REDO deletion.
|
||||||
|
15) Look for errors.
|
||||||
|
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
|
||||||
|
import azlmbr.legacy.general as general
|
||||||
|
import azlmbr.math as math
|
||||||
|
import azlmbr.render as render
|
||||||
|
|
||||||
|
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 Reflection Probe entity with no components.
|
||||||
|
reflection_probe_name = "Reflection Probe"
|
||||||
|
reflection_probe_entity = EditorEntity.create_editor_entity_at(
|
||||||
|
math.Vector3(512.0, 512.0, 34.0), reflection_probe_name)
|
||||||
|
Report.critical_result(Tests.reflection_probe_creation, reflection_probe_entity.exists())
|
||||||
|
|
||||||
|
# 2. Add a Reflection Probe component to Reflection Probe entity.
|
||||||
|
reflection_probe_component = reflection_probe_entity.add_component(reflection_probe_name)
|
||||||
|
Report.critical_result(
|
||||||
|
Tests.reflection_probe_component,
|
||||||
|
reflection_probe_entity.has_component(reflection_probe_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 reflection_probe_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, reflection_probe_entity.exists())
|
||||||
|
|
||||||
|
# 5. Verify Reflection Probe component not enabled.
|
||||||
|
Report.result(Tests.reflection_probe_disabled, not reflection_probe_component.is_enabled())
|
||||||
|
|
||||||
|
# 6. Add Box Shape component since it is required by the Reflection Probe component.
|
||||||
|
box_shape = "Box Shape"
|
||||||
|
reflection_probe_entity.add_component(box_shape)
|
||||||
|
Report.result(Tests.box_shape_component, reflection_probe_entity.has_component(box_shape))
|
||||||
|
|
||||||
|
# 7. Verify Reflection Probe component is enabled.
|
||||||
|
Report.result(Tests.reflection_probe_enabled, reflection_probe_component.is_enabled())
|
||||||
|
|
||||||
|
# 8. 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)
|
||||||
|
|
||||||
|
# 9. Test IsHidden.
|
||||||
|
reflection_probe_entity.set_visibility_state(False)
|
||||||
|
Report.result(Tests.is_hidden, reflection_probe_entity.is_hidden() is True)
|
||||||
|
|
||||||
|
# 10. Test IsVisible.
|
||||||
|
reflection_probe_entity.set_visibility_state(True)
|
||||||
|
general.idle_wait_frames(1)
|
||||||
|
Report.result(Tests.is_visible, reflection_probe_entity.is_visible() is True)
|
||||||
|
|
||||||
|
# 11. Verify cubemap generation
|
||||||
|
render.EditorReflectionProbeBus(azlmbr.bus.Event, "BakeReflectionProbe", reflection_probe_entity.id)
|
||||||
|
Report.result(
|
||||||
|
Tests.reflection_map_generated,
|
||||||
|
helper.wait_for_condition(
|
||||||
|
lambda: reflection_probe_component.get_component_property_value("Cubemap|Baked Cubemap Path") != "",
|
||||||
|
20.0))
|
||||||
|
|
||||||
|
# 12. Delete Reflection Probe entity.
|
||||||
|
reflection_probe_entity.delete()
|
||||||
|
Report.result(Tests.entity_deleted, not reflection_probe_entity.exists())
|
||||||
|
|
||||||
|
# 13. UNDO deletion.
|
||||||
|
general.undo()
|
||||||
|
Report.result(Tests.deletion_undo, reflection_probe_entity.exists())
|
||||||
|
|
||||||
|
# 14. REDO deletion.
|
||||||
|
general.redo()
|
||||||
|
Report.result(Tests.deletion_redo, not reflection_probe_entity.exists())
|
||||||
|
|
||||||
|
# 15. 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_ReflectionProbe_AddedToEntity)
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:827a63985273229050bf4f63030bcc666f045091fe81cf8157a9ca23b40074b6
|
||||||
|
size 3214
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:d8d24963e6e8765205bc79cbe2304fc39f1245ee75249e2834a71c96c3cab824
|
||||||
|
size 22700
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"$type": "ScriptProcessorRule",
|
||||||
|
"scriptFilename": "Editor/Scripts/scene_mesh_to_prefab.py"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -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);
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||||
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <AzCore/Threading/ThreadUtils.h>
|
||||||
|
#include <AzCore/std/parallel/thread.h>
|
||||||
|
#include <AzCore/Math/MathUtils.h>
|
||||||
|
|
||||||
|
namespace AZ::Threading
|
||||||
|
{
|
||||||
|
uint32_t CalcNumWorkerThreads(float workerThreadsRatio, uint32_t minNumWorkerThreads, uint32_t reservedNumThreads)
|
||||||
|
{
|
||||||
|
const uint32_t maxHardwareThreads = AZStd::thread::hardware_concurrency();
|
||||||
|
const uint32_t numReservedThreads = AZ::GetMin<uint32_t>(reservedNumThreads, maxHardwareThreads); // protect against num reserved being bigger than the number of hw threads
|
||||||
|
const uint32_t maxWorkerThreads = maxHardwareThreads - numReservedThreads;
|
||||||
|
const float requestedWorkerThreads = AZ::GetClamp<float>(workerThreadsRatio, 0.0f, 1.0f) * static_cast<float>(maxWorkerThreads);
|
||||||
|
const uint32_t requestedWorkerThreadsRounded = AZStd::lround(requestedWorkerThreads);
|
||||||
|
const uint32_t numWorkerThreads = AZ::GetMax<uint32_t>(minNumWorkerThreads, requestedWorkerThreadsRounded);
|
||||||
|
return numWorkerThreads;
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* 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/base.h>
|
||||||
|
|
||||||
|
namespace AZ::Threading
|
||||||
|
{
|
||||||
|
//! Calculates the number of worker threads a system should use based on the number of hardware threads a device has.
|
||||||
|
//! result = max (minNumWorkerThreads, workerThreadsRatio * (num_hardware_threads - reservedNumThreads))
|
||||||
|
//! @param workerThreadsRatio scale applied to the calculated maximum number of threads available after reserved threads have been accounted for. Clamped between 0 and 1.
|
||||||
|
//! @param minNumWorkerThreads minimum value that will be returned. Value is unclamped and can be more than num_hardware_threads.
|
||||||
|
//! @param reservedNumThreads number of hardware threads to reserve for O3DE system threads. Value clamped to num_hardware_threads.
|
||||||
|
//! @return number of worker threads for the calling system to allocate
|
||||||
|
uint32_t CalcNumWorkerThreads(float workerThreadsRatio, uint32_t minNumWorkerThreads, uint32_t reservedNumThreads);
|
||||||
|
};
|
||||||
@ -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"}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||||
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AzCore/RTTI/ReflectContext.h>
|
||||||
|
#include <AzCore/std/string/string.h>
|
||||||
|
#include <AzFramework/Matchmaking/MatchmakingRequests.h>
|
||||||
|
|
||||||
|
namespace AzFramework
|
||||||
|
{
|
||||||
|
//! IMatchmakingRequests
|
||||||
|
//! Pure virtual session interface class to abstract the details of session handling from application code.
|
||||||
|
class IMatchmakingRequests
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AZ_RTTI(IMatchmakingRequests, "{BC0B74DA-A448-4F40-9B50-9D73142829D5}");
|
||||||
|
|
||||||
|
IMatchmakingRequests() = default;
|
||||||
|
virtual ~IMatchmakingRequests() = default;
|
||||||
|
|
||||||
|
// Registers a player's acceptance or rejection of a proposed matchmaking.
|
||||||
|
// @param acceptMatchRequest The request of AcceptMatch operation
|
||||||
|
virtual void AcceptMatch(const AcceptMatchRequest& acceptMatchRequest) = 0;
|
||||||
|
|
||||||
|
// Create a game match for a group of players.
|
||||||
|
// @param startMatchmakingRequest The request of StartMatchmaking operation
|
||||||
|
// @return A unique identifier for a matchmaking ticket
|
||||||
|
virtual AZStd::string StartMatchmaking(const StartMatchmakingRequest& startMatchmakingRequest) = 0;
|
||||||
|
|
||||||
|
// Cancels a matchmaking ticket that is currently being processed.
|
||||||
|
// @param stopMatchmakingRequest The request of StopMatchmaking operation
|
||||||
|
virtual void StopMatchmaking(const StopMatchmakingRequest& stopMatchmakingRequest) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! IMatchmakingAsyncRequests
|
||||||
|
//! Async version of IMatchmakingRequests
|
||||||
|
class IMatchmakingAsyncRequests
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AZ_RTTI(ISessionAsyncRequests, "{53513480-2D02-493C-B44E-96AA27F42429}");
|
||||||
|
|
||||||
|
IMatchmakingAsyncRequests() = default;
|
||||||
|
virtual ~IMatchmakingAsyncRequests() = default;
|
||||||
|
|
||||||
|
// AcceptMatch Async
|
||||||
|
// @param acceptMatchRequest The request of AcceptMatch operation
|
||||||
|
virtual void AcceptMatchAsync(const AcceptMatchRequest& acceptMatchRequest) = 0;
|
||||||
|
|
||||||
|
// StartMatchmaking Async
|
||||||
|
// @param startMatchmakingRequest The request of StartMatchmaking operation
|
||||||
|
virtual void StartMatchmakingAsync(const StartMatchmakingRequest& startMatchmakingRequest) = 0;
|
||||||
|
|
||||||
|
// StopMatchmaking Async
|
||||||
|
// @param stopMatchmakingRequest The request of StopMatchmaking operation
|
||||||
|
virtual void StopMatchmakingAsync(const StopMatchmakingRequest& stopMatchmakingRequest) = 0;
|
||||||
|
};
|
||||||
|
} // namespace AzFramework
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* 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/EBus/EBus.h>
|
||||||
|
#include <AzCore/std/string/string.h>
|
||||||
|
|
||||||
|
namespace AzFramework
|
||||||
|
{
|
||||||
|
//! MatchmakingAsyncRequestNotifications
|
||||||
|
//! The notifications correspond to matchmaking async requests
|
||||||
|
class MatchmakingAsyncRequestNotifications
|
||||||
|
: public AZ::EBusTraits
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Safeguard handler for multi-threaded use case
|
||||||
|
using MutexType = AZStd::recursive_mutex;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// EBusTraits overrides
|
||||||
|
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
|
||||||
|
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// OnAcceptMatchAsyncComplete is fired once AcceptMatchAsync completes
|
||||||
|
virtual void OnAcceptMatchAsyncComplete() = 0;
|
||||||
|
|
||||||
|
// OnStartMatchmakingAsyncComplete is fired once StartMatchmakingAsync completes
|
||||||
|
// @param matchmakingTicketId The unique identifier for the matchmaking ticket
|
||||||
|
virtual void OnStartMatchmakingAsyncComplete(const AZStd::string& matchmakingTicketId) = 0;
|
||||||
|
|
||||||
|
// OnStopMatchmakingAsyncComplete is fired once StopMatchmakingAsync completes
|
||||||
|
virtual void OnStopMatchmakingAsyncComplete() = 0;
|
||||||
|
};
|
||||||
|
using MatchmakingAsyncRequestNotificationBus = AZ::EBus<MatchmakingAsyncRequestNotifications>;
|
||||||
|
|
||||||
|
//! MatchmakingNotifications
|
||||||
|
//! The matchmaking notifications to listen for performing required operations
|
||||||
|
class MatchAcceptanceNotifications
|
||||||
|
: public AZ::EBusTraits
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Safeguard handler for multi-threaded use case
|
||||||
|
using MutexType = AZStd::recursive_mutex;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// EBusTraits overrides
|
||||||
|
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
|
||||||
|
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// OnMatchAcceptance is fired when DescribeMatchmaking ticket status is REQUIRES_ACCEPTANCE
|
||||||
|
virtual void OnMatchAcceptance() = 0;
|
||||||
|
};
|
||||||
|
using MatchAcceptanceNotificationBus = AZ::EBus<MatchAcceptanceNotifications>;
|
||||||
|
} // namespace AzFramework
|
||||||
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* 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/RTTI/ReflectContext.h>
|
||||||
|
#include <AzCore/Serialization/EditContext.h>
|
||||||
|
#include <AzCore/Serialization/SerializeContext.h>
|
||||||
|
#include <AzFramework/Matchmaking/MatchmakingRequests.h>
|
||||||
|
|
||||||
|
namespace AzFramework
|
||||||
|
{
|
||||||
|
void AcceptMatchRequest::Reflect(AZ::ReflectContext* context)
|
||||||
|
{
|
||||||
|
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||||
|
{
|
||||||
|
serializeContext->Class<AcceptMatchRequest>()
|
||||||
|
->Version(0)
|
||||||
|
->Field("acceptMatch", &AcceptMatchRequest::m_acceptMatch)
|
||||||
|
->Field("playerIds", &AcceptMatchRequest::m_playerIds)
|
||||||
|
->Field("ticketId", &AcceptMatchRequest::m_ticketId);
|
||||||
|
|
||||||
|
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
|
||||||
|
{
|
||||||
|
editContext->Class<AcceptMatchRequest>("AcceptMatchRequest", "The container for AcceptMatch request parameters")
|
||||||
|
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||||
|
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
|
||||||
|
->DataElement(AZ::Edit::UIHandlers::Default, &AcceptMatchRequest::m_acceptMatch, "AcceptMatch",
|
||||||
|
"Player response to accept or reject match")
|
||||||
|
->DataElement(AZ::Edit::UIHandlers::Default, &AcceptMatchRequest::m_playerIds, "PlayerIds",
|
||||||
|
"A list of unique identifiers for players delivering the response")
|
||||||
|
->DataElement(AZ::Edit::UIHandlers::Default, &AcceptMatchRequest::m_ticketId, "TicketId",
|
||||||
|
"A unique identifier for a matchmaking ticket");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StartMatchmakingRequest::Reflect(AZ::ReflectContext* context)
|
||||||
|
{
|
||||||
|
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||||
|
{
|
||||||
|
serializeContext->Class<StartMatchmakingRequest>()
|
||||||
|
->Version(0)
|
||||||
|
->Field("ticketId", &StartMatchmakingRequest::m_ticketId);
|
||||||
|
|
||||||
|
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
|
||||||
|
{
|
||||||
|
editContext->Class<StartMatchmakingRequest>("StartMatchmakingRequest", "The container for StartMatchmaking request parameters")
|
||||||
|
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||||
|
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
|
||||||
|
->DataElement(AZ::Edit::UIHandlers::Default, &StartMatchmakingRequest::m_ticketId, "TicketId",
|
||||||
|
"A unique identifier for a matchmaking ticket");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StopMatchmakingRequest::Reflect(AZ::ReflectContext* context)
|
||||||
|
{
|
||||||
|
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||||
|
{
|
||||||
|
serializeContext->Class<StopMatchmakingRequest>()
|
||||||
|
->Version(0)
|
||||||
|
->Field("ticketId", &StopMatchmakingRequest::m_ticketId);
|
||||||
|
|
||||||
|
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
|
||||||
|
{
|
||||||
|
editContext->Class<StopMatchmakingRequest>("StopMatchmakingRequest", "The container for StopMatchmaking request parameters")
|
||||||
|
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||||
|
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
|
||||||
|
->DataElement(AZ::Edit::UIHandlers::Default, &StopMatchmakingRequest::m_ticketId, "TicketId",
|
||||||
|
"A unique identifier for a matchmaking ticket");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace AzFramework
|
||||||
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||||
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AzCore/RTTI/RTTI.h>
|
||||||
|
#include <AzCore/std/string/string.h>
|
||||||
|
|
||||||
|
namespace AZ
|
||||||
|
{
|
||||||
|
class ReflectContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace AzFramework
|
||||||
|
{
|
||||||
|
//! AcceptMatchRequest
|
||||||
|
//! The container for AcceptMatch request parameters.
|
||||||
|
struct AcceptMatchRequest
|
||||||
|
{
|
||||||
|
AZ_RTTI(AcceptMatchRequest, "{AD289D76-CEE2-424F-847E-E62AA83B7D79}");
|
||||||
|
static void Reflect(AZ::ReflectContext* context);
|
||||||
|
|
||||||
|
AcceptMatchRequest() = default;
|
||||||
|
virtual ~AcceptMatchRequest() = default;
|
||||||
|
|
||||||
|
// Player response to accept or reject match
|
||||||
|
bool m_acceptMatch;
|
||||||
|
// A list of unique identifiers for players delivering the response
|
||||||
|
AZStd::vector<AZStd::string> m_playerIds;
|
||||||
|
// A unique identifier for a matchmaking ticket
|
||||||
|
AZStd::string m_ticketId;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! StartMatchmakingRequest
|
||||||
|
//! The container for StartMatchmaking request parameters.
|
||||||
|
struct StartMatchmakingRequest
|
||||||
|
{
|
||||||
|
AZ_RTTI(StartMatchmakingRequest, "{70B47776-E8E7-4993-BEC3-5CAEC3D48E47}");
|
||||||
|
static void Reflect(AZ::ReflectContext* context);
|
||||||
|
|
||||||
|
StartMatchmakingRequest() = default;
|
||||||
|
virtual ~StartMatchmakingRequest() = default;
|
||||||
|
|
||||||
|
// A unique identifier for a matchmaking ticket
|
||||||
|
AZStd::string m_ticketId;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! StopMatchmakingRequest
|
||||||
|
//! The container for StopMatchmaking request parameters.
|
||||||
|
struct StopMatchmakingRequest
|
||||||
|
{
|
||||||
|
AZ_RTTI(StopMatchmakingRequest, "{6132E293-65EF-4DC2-A8A0-00269697229D}");
|
||||||
|
static void Reflect(AZ::ReflectContext* context);
|
||||||
|
|
||||||
|
StopMatchmakingRequest() = default;
|
||||||
|
virtual ~StopMatchmakingRequest() = default;
|
||||||
|
|
||||||
|
// A unique identifier for a matchmaking ticket
|
||||||
|
AZStd::string m_ticketId;
|
||||||
|
};
|
||||||
|
} // namespace AzFramework
|
||||||
@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||||
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AzCore/RTTI/RTTI.h>
|
||||||
|
#include <AzCore/std/containers/unordered_map.h>
|
||||||
|
#include <AzCore/std/string/string.h>
|
||||||
|
|
||||||
|
namespace AZ
|
||||||
|
{
|
||||||
|
class ReflectContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace AzFramework
|
||||||
|
{
|
||||||
|
struct SessionConfig;
|
||||||
|
|
||||||
|
//! CreateSessionRequest
|
||||||
|
//! The container for CreateSession request parameters.
|
||||||
|
struct CreateSessionRequest
|
||||||
|
{
|
||||||
|
AZ_RTTI(CreateSessionRequest, "{E39C2A45-89C9-4CFB-B337-9734DC798930}");
|
||||||
|
static void Reflect(AZ::ReflectContext* context);
|
||||||
|
|
||||||
|
CreateSessionRequest() = default;
|
||||||
|
virtual ~CreateSessionRequest() = default;
|
||||||
|
|
||||||
|
// A unique identifier for a player or entity creating the session.
|
||||||
|
AZStd::string m_creatorId;
|
||||||
|
|
||||||
|
// A collection of custom properties for a session.
|
||||||
|
AZStd::unordered_map<AZStd::string, AZStd::string> m_sessionProperties;
|
||||||
|
|
||||||
|
// A descriptive label that is associated with a session.
|
||||||
|
AZStd::string m_sessionName;
|
||||||
|
|
||||||
|
// The maximum number of players that can be connected simultaneously to the session.
|
||||||
|
uint64_t m_maxPlayer = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! SearchSessionsRequest
|
||||||
|
//! The container for SearchSessions request parameters.
|
||||||
|
struct SearchSessionsRequest
|
||||||
|
{
|
||||||
|
AZ_RTTI(SearchSessionsRequest, "{B49207A8-8549-4ADB-B7D9-D7A4932F9B4B}");
|
||||||
|
static void Reflect(AZ::ReflectContext* context);
|
||||||
|
|
||||||
|
SearchSessionsRequest() = default;
|
||||||
|
virtual ~SearchSessionsRequest() = default;
|
||||||
|
|
||||||
|
// String containing the search criteria for the session search. If no filter expression is included, the request returns results
|
||||||
|
// for all active sessions.
|
||||||
|
AZStd::string m_filterExpression;
|
||||||
|
|
||||||
|
// Instructions on how to sort the search results. If no sort expression is included, the request returns results in random order.
|
||||||
|
AZStd::string m_sortExpression;
|
||||||
|
|
||||||
|
// The maximum number of results to return.
|
||||||
|
uint8_t m_maxResult = 0;
|
||||||
|
|
||||||
|
// A token that indicates the start of the next sequential page of results.
|
||||||
|
AZStd::string m_nextToken;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! SearchSessionsResponse
|
||||||
|
//! The container for SearchSession request results.
|
||||||
|
struct SearchSessionsResponse
|
||||||
|
{
|
||||||
|
AZ_RTTI(SearchSessionsResponse, "{F93DE7DC-D381-4E08-8A3B-0B08F7C38714}");
|
||||||
|
static void Reflect(AZ::ReflectContext* context);
|
||||||
|
|
||||||
|
SearchSessionsResponse() = default;
|
||||||
|
virtual ~SearchSessionsResponse() = default;
|
||||||
|
|
||||||
|
// A collection of sessions that match the search criteria and sorted in specific order.
|
||||||
|
AZStd::vector<SessionConfig> m_sessionConfigs;
|
||||||
|
|
||||||
|
// A token that indicates the start of the next sequential page of results.
|
||||||
|
AZStd::string m_nextToken;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! JoinSessionRequest
|
||||||
|
//! The container for JoinSession request parameters.
|
||||||
|
struct JoinSessionRequest
|
||||||
|
{
|
||||||
|
AZ_RTTI(JoinSessionRequest, "{519769E8-3CDE-4385-A0D7-24DBB3685657}");
|
||||||
|
static void Reflect(AZ::ReflectContext* context);
|
||||||
|
|
||||||
|
JoinSessionRequest() = default;
|
||||||
|
virtual ~JoinSessionRequest() = default;
|
||||||
|
|
||||||
|
// A unique identifier for the session.
|
||||||
|
AZStd::string m_sessionId;
|
||||||
|
|
||||||
|
// A unique identifier for a player. Player IDs are developer-defined.
|
||||||
|
AZStd::string m_playerId;
|
||||||
|
|
||||||
|
// Developer-defined information related to a player.
|
||||||
|
AZStd::string m_playerData;
|
||||||
|
};
|
||||||
|
} // namespace AzFramework
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* 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 <gmock/gmock.h>
|
||||||
|
#include <AzCore/std/string/string.h>
|
||||||
|
|
||||||
|
inline testing::PolymorphicMatcher<testing::internal::StrEqualityMatcher<AZStd::string>> StrEq(const AZStd::string& str)
|
||||||
|
{
|
||||||
|
return ::testing::MakePolymorphicMatcher(testing::internal::StrEqualityMatcher<AZStd::string>(str, true, true));
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||||
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "XcbBaseTestFixture.h"
|
||||||
|
|
||||||
|
namespace AzFramework
|
||||||
|
{
|
||||||
|
void XcbBaseTestFixture::SetUp()
|
||||||
|
{
|
||||||
|
using testing::Return;
|
||||||
|
using testing::_;
|
||||||
|
|
||||||
|
testing::Test::SetUp();
|
||||||
|
|
||||||
|
EXPECT_CALL(m_interface, xcb_connect(_, _))
|
||||||
|
.WillOnce(Return(&m_connection));
|
||||||
|
EXPECT_CALL(m_interface, xcb_disconnect(&m_connection))
|
||||||
|
.Times(1);
|
||||||
|
}
|
||||||
|
} // namespace AzFramework
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* 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 <gtest/gtest.h>
|
||||||
|
#include <xcb/xcb.h>
|
||||||
|
|
||||||
|
#include "MockXcbInterface.h"
|
||||||
|
|
||||||
|
namespace AzFramework
|
||||||
|
{
|
||||||
|
// Sets up mock behavior for the xcb library, providing an xcb_connection_t that is returned from a call to xcb_connect
|
||||||
|
class XcbBaseTestFixture
|
||||||
|
: public testing::Test
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void SetUp() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
testing::NiceMock<MockXcbInterface> m_interface;
|
||||||
|
xcb_connection_t m_connection{};
|
||||||
|
};
|
||||||
|
} // namespace AzFramework
|
||||||
@ -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());
|
||||||
|
}
|
||||||
@ -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
|
||||||
@ -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
|
||||||
@ -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>
|
||||||
@ -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>
|
||||||
@ -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
|
||||||
@ -0,0 +1,351 @@
|
|||||||
|
/*
|
||||||
|
* 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 <sstream>
|
||||||
|
#include <AzCore/JSON/rapidjson.h>
|
||||||
|
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||||
|
#include <AzCore/Serialization/Json/JsonSerializationSettings.h>
|
||||||
|
#include <AzCore/Serialization/Json/JsonUtils.h>
|
||||||
|
#include <AzFramework/Entity/EntityContext.h>
|
||||||
|
#include <AzFramework/FileFunc/FileFunc.h>
|
||||||
|
#include <AzToolsFramework/Entity/EntityUtilityComponent.h>
|
||||||
|
#include <Entity/EditorEntityContextBus.h>
|
||||||
|
#include <rapidjson/document.h>
|
||||||
|
|
||||||
|
namespace AzToolsFramework
|
||||||
|
{
|
||||||
|
void ComponentDetails::Reflect(AZ::ReflectContext* context)
|
||||||
|
{
|
||||||
|
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||||
|
{
|
||||||
|
serializeContext->Class<ComponentDetails>()
|
||||||
|
->Field("TypeInfo", &ComponentDetails::m_typeInfo)
|
||||||
|
->Field("BaseClasses", &ComponentDetails::m_baseClasses);
|
||||||
|
|
||||||
|
serializeContext->RegisterGenericType<AZStd::vector<ComponentDetails>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
||||||
|
{
|
||||||
|
behaviorContext->Class<ComponentDetails>()
|
||||||
|
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
|
||||||
|
->Attribute(AZ::Script::Attributes::Module, "entity")
|
||||||
|
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
|
||||||
|
->Property("TypeInfo", BehaviorValueProperty(&ComponentDetails::m_typeInfo))
|
||||||
|
->Property("BaseClasses", BehaviorValueProperty(&ComponentDetails::m_baseClasses))
|
||||||
|
->Method("__repr__", [](const ComponentDetails& obj)
|
||||||
|
{
|
||||||
|
std::ostringstream result;
|
||||||
|
bool first = true;
|
||||||
|
|
||||||
|
for (const auto& baseClass : obj.m_baseClasses)
|
||||||
|
{
|
||||||
|
if (!first)
|
||||||
|
{
|
||||||
|
result << ", ";
|
||||||
|
}
|
||||||
|
|
||||||
|
first = false;
|
||||||
|
result << baseClass.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
return AZStd::string::format("%s, Base Classes: <%s>", obj.m_typeInfo.c_str(), result.str().c_str());
|
||||||
|
})
|
||||||
|
->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::ToString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AZ::EntityId EntityUtilityComponent::CreateEditorReadyEntity(const AZStd::string& entityName)
|
||||||
|
{
|
||||||
|
auto* newEntity = m_entityContext->CreateEntity(entityName.c_str());
|
||||||
|
|
||||||
|
if (!newEntity)
|
||||||
|
{
|
||||||
|
AZ_Error("EditorEntityUtility", false, "Failed to create new entity %s", entityName.c_str());
|
||||||
|
return AZ::EntityId();
|
||||||
|
}
|
||||||
|
|
||||||
|
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
|
||||||
|
&AzToolsFramework::EditorEntityContextRequestBus::Events::AddRequiredComponents, *newEntity);
|
||||||
|
|
||||||
|
newEntity->Init();
|
||||||
|
auto newEntityId = newEntity->GetId();
|
||||||
|
|
||||||
|
m_createdEntities.emplace_back(newEntityId);
|
||||||
|
|
||||||
|
return newEntityId;
|
||||||
|
}
|
||||||
|
|
||||||
|
AZ::TypeId GetComponentTypeIdFromName(const AZStd::string& typeName)
|
||||||
|
{
|
||||||
|
// Try to create a TypeId first. We won't show any warnings if this fails as the input might be a class name instead
|
||||||
|
AZ::TypeId typeId = AZ::TypeId::CreateStringPermissive(typeName.data());
|
||||||
|
|
||||||
|
// If the typeId is null, try a lookup by class name
|
||||||
|
if (typeId.IsNull())
|
||||||
|
{
|
||||||
|
AZ::SerializeContext* serializeContext = nullptr;
|
||||||
|
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
|
||||||
|
|
||||||
|
auto typeNameCrc = AZ::Crc32(typeName.data());
|
||||||
|
auto typeUuidList = serializeContext->FindClassId(typeNameCrc);
|
||||||
|
|
||||||
|
// TypeId is invalid or class name is invalid
|
||||||
|
if (typeUuidList.empty())
|
||||||
|
{
|
||||||
|
AZ_Error("EntityUtilityComponent", false, "Provided type %s is either an invalid TypeId or does not match any class names", typeName.c_str());
|
||||||
|
return AZ::TypeId::CreateNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
typeId = typeUuidList[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return typeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
AZ::Component* FindComponentHelper(AZ::EntityId entityId, const AZ::TypeId& typeId, AZ::ComponentId componentId, bool createComponent = false)
|
||||||
|
{
|
||||||
|
AZ::Entity* entity = nullptr;
|
||||||
|
AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, entityId);
|
||||||
|
|
||||||
|
if (!entity)
|
||||||
|
{
|
||||||
|
AZ_Error("EntityUtilityComponent", false, "Invalid entityId %s", entityId.ToString().c_str());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
AZ::Component* component = nullptr;
|
||||||
|
if (componentId != AZ::InvalidComponentId)
|
||||||
|
{
|
||||||
|
component = entity->FindComponent(componentId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
component = entity->FindComponent(typeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!component && createComponent)
|
||||||
|
{
|
||||||
|
component = entity->CreateComponent(typeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!component)
|
||||||
|
{
|
||||||
|
AZ_Error(
|
||||||
|
"EntityUtilityComponent", false, "Failed to find component (%s) on entity %s (%s)",
|
||||||
|
componentId != AZ::InvalidComponentId ? AZStd::to_string(componentId).c_str()
|
||||||
|
: typeId.ToString<AZStd::string>().c_str(),
|
||||||
|
entityId.ToString().c_str(),
|
||||||
|
entity->GetName().c_str());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return component;
|
||||||
|
}
|
||||||
|
|
||||||
|
AzFramework::BehaviorComponentId EntityUtilityComponent::GetOrAddComponentByTypeName(AZ::EntityId entityId, const AZStd::string& typeName)
|
||||||
|
{
|
||||||
|
AZ::TypeId typeId = GetComponentTypeIdFromName(typeName);
|
||||||
|
|
||||||
|
if (typeId.IsNull())
|
||||||
|
{
|
||||||
|
return AzFramework::BehaviorComponentId(AZ::InvalidComponentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
AZ::Component* component = FindComponentHelper(entityId, typeId, AZ::InvalidComponentId, true);
|
||||||
|
|
||||||
|
return component ? AzFramework::BehaviorComponentId(component->GetId()) :
|
||||||
|
AzFramework::BehaviorComponentId(AZ::InvalidComponentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EntityUtilityComponent::UpdateComponentForEntity(AZ::EntityId entityId, AzFramework::BehaviorComponentId componentId, const AZStd::string& json)
|
||||||
|
{
|
||||||
|
if (!componentId.IsValid())
|
||||||
|
{
|
||||||
|
AZ_Error("EntityUtilityComponent", false, "Invalid componentId passed to UpdateComponentForEntity");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AZ::Component* component = FindComponentHelper(entityId, AZ::TypeId::CreateNull(), componentId);
|
||||||
|
|
||||||
|
if (!component)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace AZ::JsonSerializationResult;
|
||||||
|
|
||||||
|
AZ::JsonDeserializerSettings settings = AZ::JsonDeserializerSettings{};
|
||||||
|
settings.m_reporting = []([[maybe_unused]] AZStd::string_view message, ResultCode result, AZStd::string_view) -> auto
|
||||||
|
{
|
||||||
|
if (result.GetProcessing() == Processing::Halted)
|
||||||
|
{
|
||||||
|
AZ_Error("EntityUtilityComponent", false, "JSON %s\n", message.data());
|
||||||
|
}
|
||||||
|
else if (result.GetOutcome() > Outcomes::PartialDefaults)
|
||||||
|
{
|
||||||
|
AZ_Warning("EntityUtilityComponent", false, "JSON %s\n", message.data());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
rapidjson::Document doc;
|
||||||
|
doc.Parse<rapidjson::kParseCommentsFlag>(json.data(), json.size());
|
||||||
|
ResultCode resultCode = AZ::JsonSerialization::Load(*component, doc, settings);
|
||||||
|
|
||||||
|
return resultCode.GetProcessing() != Processing::Halted;
|
||||||
|
}
|
||||||
|
|
||||||
|
AZStd::string EntityUtilityComponent::GetComponentDefaultJson(const AZStd::string& typeName)
|
||||||
|
{
|
||||||
|
AZ::TypeId typeId = GetComponentTypeIdFromName(typeName);
|
||||||
|
|
||||||
|
if (typeId.IsNull())
|
||||||
|
{
|
||||||
|
// GetComponentTypeIdFromName already does error handling
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
AZ::SerializeContext* serializeContext = nullptr;
|
||||||
|
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
|
||||||
|
|
||||||
|
const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(typeId);
|
||||||
|
|
||||||
|
if (!classData)
|
||||||
|
{
|
||||||
|
AZ_Error("EntityUtilityComponent", false, "Failed to find ClassData for typeId %s (%s)", typeId.ToString<AZStd::string>().c_str(), typeName.c_str());
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
void* component = classData->m_factory->Create("Component");
|
||||||
|
rapidjson::Document document;
|
||||||
|
AZ::JsonSerializerSettings settings;
|
||||||
|
settings.m_keepDefaults = true;
|
||||||
|
|
||||||
|
auto resultCode = AZ::JsonSerialization::Store(document, document.GetAllocator(), component, nullptr, typeId, settings);
|
||||||
|
|
||||||
|
// Clean up the allocated component ASAP, we don't need it anymore
|
||||||
|
classData->m_factory->Destroy(component);
|
||||||
|
|
||||||
|
if (resultCode.GetProcessing() == AZ::JsonSerializationResult::Processing::Halted)
|
||||||
|
{
|
||||||
|
AZ_Error("EntityUtilityComponent", false, "Failed to serialize component to json (%s): %s",
|
||||||
|
typeName.c_str(), resultCode.ToString(typeName).c_str())
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
AZStd::string jsonString;
|
||||||
|
AZ::Outcome<void, AZStd::string> outcome = AZ::JsonSerializationUtils::WriteJsonString(document, jsonString);
|
||||||
|
|
||||||
|
if (!outcome.IsSuccess())
|
||||||
|
{
|
||||||
|
AZ_Error("EntityUtilityComponent", false, "Failed to write component json to string: %s", outcome.GetError().c_str());
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonString;
|
||||||
|
}
|
||||||
|
|
||||||
|
AZStd::vector<ComponentDetails> EntityUtilityComponent::FindMatchingComponents(const AZStd::string& searchTerm)
|
||||||
|
{
|
||||||
|
AZ::SerializeContext* serializeContext = nullptr;
|
||||||
|
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
|
||||||
|
|
||||||
|
if (m_typeInfo.empty())
|
||||||
|
{
|
||||||
|
serializeContext->EnumerateDerived<AZ::Component>(
|
||||||
|
[this, serializeContext](const AZ::SerializeContext::ClassData* classData, const AZ::Uuid& /*typeId*/)
|
||||||
|
{
|
||||||
|
auto& typeInfo = m_typeInfo.emplace_back(classData->m_typeId, classData->m_name, AZStd::vector<AZStd::string>{});
|
||||||
|
|
||||||
|
serializeContext->EnumerateBase(
|
||||||
|
[&typeInfo](const AZ::SerializeContext::ClassData* classData, const AZ::Uuid&)
|
||||||
|
{
|
||||||
|
if (classData)
|
||||||
|
{
|
||||||
|
AZStd::get<2>(typeInfo).emplace_back(classData->m_name);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
classData->m_typeId);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
AZStd::vector<ComponentDetails> matches;
|
||||||
|
|
||||||
|
for (const auto& [typeId, typeName, baseClasses] : m_typeInfo)
|
||||||
|
{
|
||||||
|
if (AZStd::wildcard_match(searchTerm, typeName))
|
||||||
|
{
|
||||||
|
ComponentDetails details;
|
||||||
|
details.m_typeInfo = AZStd::string::format("%s %s", typeId.ToString<AZStd::string>().c_str(), typeName.c_str());
|
||||||
|
details.m_baseClasses = baseClasses;
|
||||||
|
|
||||||
|
matches.emplace_back(AZStd::move(details));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityUtilityComponent::ResetEntityContext()
|
||||||
|
{
|
||||||
|
for (AZ::EntityId entityId : m_createdEntities)
|
||||||
|
{
|
||||||
|
m_entityContext->DestroyEntityById(entityId);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_createdEntities.clear();
|
||||||
|
m_entityContext->ResetContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityUtilityComponent::Reflect(AZ::ReflectContext* context)
|
||||||
|
{
|
||||||
|
ComponentDetails::Reflect(context);
|
||||||
|
|
||||||
|
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||||
|
{
|
||||||
|
serializeContext->Class<EntityUtilityComponent, AZ::Component>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
||||||
|
{
|
||||||
|
behaviorContext->ConstantProperty("InvalidComponentId", BehaviorConstant(AZ::InvalidComponentId))
|
||||||
|
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
|
||||||
|
->Attribute(AZ::Script::Attributes::Category, "Entity")
|
||||||
|
->Attribute(AZ::Script::Attributes::Module, "entity");
|
||||||
|
|
||||||
|
behaviorContext->EBus<EntityUtilityBus>("EntityUtilityBus")
|
||||||
|
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
|
||||||
|
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
|
||||||
|
->Attribute(AZ::Script::Attributes::Category, "Entity")
|
||||||
|
->Attribute(AZ::Script::Attributes::Module, "entity")
|
||||||
|
->Event("CreateEditorReadyEntity", &EntityUtilityBus::Events::CreateEditorReadyEntity)
|
||||||
|
->Event("GetOrAddComponentByTypeName", &EntityUtilityBus::Events::GetOrAddComponentByTypeName)
|
||||||
|
->Event("UpdateComponentForEntity", &EntityUtilityBus::Events::UpdateComponentForEntity)
|
||||||
|
->Event("FindMatchingComponents", &EntityUtilityBus::Events::FindMatchingComponents)
|
||||||
|
->Event("GetComponentDefaultJson", &EntityUtilityBus::Events::GetComponentDefaultJson)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityUtilityComponent::Activate()
|
||||||
|
{
|
||||||
|
m_entityContext = AZStd::make_unique<AzFramework::EntityContext>(UtilityEntityContextId);
|
||||||
|
m_entityContext->InitContext();
|
||||||
|
EntityUtilityBus::Handler::BusConnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityUtilityComponent::Deactivate()
|
||||||
|
{
|
||||||
|
EntityUtilityBus::Handler::BusDisconnect();
|
||||||
|
m_entityContext = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||||
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AzCore/Component/Component.h>
|
||||||
|
#include <AzCore/Component/Entity.h>
|
||||||
|
#include <AzCore/Serialization/SerializeContext.h>
|
||||||
|
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
|
||||||
|
#include <AzToolsFramework/ToolsComponents/EditorDisabledCompositionBus.h>
|
||||||
|
#include <AzToolsFramework/ToolsComponents/EditorPendingCompositionBus.h>
|
||||||
|
#include <AzToolsFramework/API/EntityCompositionRequestBus.h>
|
||||||
|
#include <AzCore/Component/ComponentApplication.h>
|
||||||
|
#include <AzFramework/Entity/BehaviorEntity.h>
|
||||||
|
|
||||||
|
namespace AzToolsFramework
|
||||||
|
{
|
||||||
|
struct ComponentDetails
|
||||||
|
{
|
||||||
|
AZ_TYPE_INFO(AzToolsFramework::ComponentDetails, "{107D8379-4AD4-4547-BEE1-184B120F23E9}");
|
||||||
|
|
||||||
|
static void Reflect(AZ::ReflectContext* context);
|
||||||
|
|
||||||
|
AZStd::string m_typeInfo;
|
||||||
|
AZStd::vector<AZStd::string> m_baseClasses;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This ebus is intended to provide behavior-context friendly APIs to create and manage entities
|
||||||
|
struct EntityUtilityTraits : AZ::EBusTraits
|
||||||
|
{
|
||||||
|
AZ_RTTI(AzToolsFramework::EntityUtilityTraits, "{A6305CAE-C825-43F9-A44D-E503910912AF}");
|
||||||
|
|
||||||
|
virtual ~EntityUtilityTraits() = default;
|
||||||
|
|
||||||
|
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
|
||||||
|
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
|
||||||
|
|
||||||
|
// Creates an entity with the default editor components attached and initializes the entity
|
||||||
|
virtual AZ::EntityId CreateEditorReadyEntity(const AZStd::string& entityName) = 0;
|
||||||
|
|
||||||
|
virtual AzFramework::BehaviorComponentId GetOrAddComponentByTypeName(AZ::EntityId entity, const AZStd::string& typeName) = 0;
|
||||||
|
|
||||||
|
virtual bool UpdateComponentForEntity(AZ::EntityId entity, AzFramework::BehaviorComponentId component, const AZStd::string& json) = 0;
|
||||||
|
|
||||||
|
// Gets a JSON string containing describing the default serialization state of the specified component
|
||||||
|
virtual AZStd::string GetComponentDefaultJson(const AZStd::string& typeName) = 0;
|
||||||
|
|
||||||
|
// Returns a list of matching component type names. Supports wildcard search terms
|
||||||
|
virtual AZStd::vector<ComponentDetails> FindMatchingComponents(const AZStd::string& searchTerm) = 0;
|
||||||
|
|
||||||
|
virtual void ResetEntityContext() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
using EntityUtilityBus = AZ::EBus<EntityUtilityTraits>;
|
||||||
|
|
||||||
|
struct EntityUtilityComponent : AZ::Component
|
||||||
|
, EntityUtilityBus::Handler
|
||||||
|
{
|
||||||
|
inline const static AZ::Uuid UtilityEntityContextId = AZ::Uuid("{9C277B88-E79E-4F8A-BAFF-A4C175BD565F}");
|
||||||
|
|
||||||
|
AZ_COMPONENT(EntityUtilityComponent, "{47205907-A0EA-4FFF-A620-04D20C04A379}");
|
||||||
|
|
||||||
|
AZ::EntityId CreateEditorReadyEntity(const AZStd::string& entityName) override;
|
||||||
|
AzFramework::BehaviorComponentId GetOrAddComponentByTypeName(AZ::EntityId entity, const AZStd::string& typeName) override;
|
||||||
|
bool UpdateComponentForEntity(AZ::EntityId entity, AzFramework::BehaviorComponentId component, const AZStd::string& json) override;
|
||||||
|
AZStd::string GetComponentDefaultJson(const AZStd::string& typeName) override;
|
||||||
|
AZStd::vector<ComponentDetails> FindMatchingComponents(const AZStd::string& searchTerm) override;
|
||||||
|
void ResetEntityContext() override;
|
||||||
|
|
||||||
|
static void Reflect(AZ::ReflectContext* context);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void Activate() override;
|
||||||
|
void Deactivate() override;
|
||||||
|
|
||||||
|
// Our own entity context. This API is intended mostly for use in Asset Builders where there is no editor context
|
||||||
|
// Additionally, an entity context is needed when using the Behavior Entity class
|
||||||
|
AZStd::unique_ptr<AzFramework::EntityContext> m_entityContext;
|
||||||
|
|
||||||
|
// TypeId, TypeName, Vector<BaseClassName>
|
||||||
|
AZStd::vector<AZStd::tuple<AZ::TypeId, AZStd::string, AZStd::vector<AZStd::string>>> m_typeInfo;
|
||||||
|
|
||||||
|
// Keep track of the entities we create so they can be reset
|
||||||
|
AZStd::vector<AZ::EntityId> m_createdEntities;
|
||||||
|
};
|
||||||
|
}; // namespace AzToolsFramework
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||||
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AzCore/Interface/Interface.h>
|
||||||
|
#include <AzCore/IO/Path/Path.h>
|
||||||
|
#include <AzCore/Serialization/SerializeContext.h>
|
||||||
|
#include <AzToolsFramework/Prefab/PrefabIdTypes.h>
|
||||||
|
#include <AzCore/EBus/EBus.h>
|
||||||
|
|
||||||
|
namespace AzToolsFramework
|
||||||
|
{
|
||||||
|
namespace Prefab
|
||||||
|
{
|
||||||
|
// Ebus for script-friendly APIs for the prefab loader
|
||||||
|
struct PrefabLoaderScriptingTraits : AZ::EBusTraits
|
||||||
|
{
|
||||||
|
AZ_TYPE_INFO(PrefabLoaderScriptingTraits, "{C344B7D8-8299-48C9-8450-26E1332EA011}");
|
||||||
|
|
||||||
|
virtual ~PrefabLoaderScriptingTraits() = default;
|
||||||
|
|
||||||
|
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
|
||||||
|
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves a Prefab Template into the provided output string.
|
||||||
|
* Converts Prefab Template form into .prefab form by collapsing nested Template info
|
||||||
|
* into a source path and patches.
|
||||||
|
* @param templateId Id of the template to be saved
|
||||||
|
* @return Will contain the serialized template json on success
|
||||||
|
*/
|
||||||
|
virtual AZ::Outcome<AZStd::string, void> SaveTemplateToString(TemplateId templateId) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
using PrefabLoaderScriptingBus = AZ::EBus<PrefabLoaderScriptingTraits>;
|
||||||
|
|
||||||
|
} // namespace Prefab
|
||||||
|
} // namespace AzToolsFramework
|
||||||
|
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||||
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AzCore/Interface/Interface.h>
|
||||||
|
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||||
|
#include <AzToolsFramework/Prefab/Link/Link.h>
|
||||||
|
#include <AzToolsFramework/Prefab/PrefabIdTypes.h>
|
||||||
|
#include <AzToolsFramework/Prefab/Template/Template.h>
|
||||||
|
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||||
|
#include <AzCore/EBus/EBus.h>
|
||||||
|
|
||||||
|
namespace AzToolsFramework
|
||||||
|
{
|
||||||
|
namespace Prefab
|
||||||
|
{
|
||||||
|
// Bus that exposes a script-friendly interface to the PrefabSystemComponent
|
||||||
|
struct PrefabSystemScriptingEbusTraits : AZ::EBusTraits
|
||||||
|
{
|
||||||
|
using MutexType = AZ::NullMutex;
|
||||||
|
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
|
||||||
|
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
|
||||||
|
|
||||||
|
virtual TemplateId CreatePrefabTemplate(
|
||||||
|
const AZStd::vector<AZ::EntityId>& entityIds, const AZStd::string& filePath) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
using PrefabSystemScriptingBus = AZ::EBus<PrefabSystemScriptingEbusTraits>;
|
||||||
|
|
||||||
|
} // namespace Prefab
|
||||||
|
} // namespace AzToolsFramework
|
||||||
|
|
||||||
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* 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/Component/ComponentApplicationBus.h>
|
||||||
|
#include <AzCore/RTTI/BehaviorContext.h>
|
||||||
|
#include <Prefab/PrefabSystemComponentInterface.h>
|
||||||
|
#include <Prefab/PrefabSystemScriptingHandler.h>
|
||||||
|
#include <AzCore/Component/Entity.h>
|
||||||
|
|
||||||
|
namespace AzToolsFramework::Prefab
|
||||||
|
{
|
||||||
|
void PrefabSystemScriptingHandler::Reflect(AZ::ReflectContext* context)
|
||||||
|
{
|
||||||
|
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
||||||
|
{
|
||||||
|
behaviorContext->ConstantProperty("InvalidTemplateId", BehaviorConstant(InvalidTemplateId))
|
||||||
|
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
|
||||||
|
->Attribute(AZ::Script::Attributes::Module, "prefab")
|
||||||
|
->Attribute(AZ::Script::Attributes::Category, "Prefab");
|
||||||
|
|
||||||
|
behaviorContext->EBus<PrefabSystemScriptingBus>("PrefabSystemScriptingBus")
|
||||||
|
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
|
||||||
|
->Attribute(AZ::Script::Attributes::Module, "prefab")
|
||||||
|
->Attribute(AZ::Script::Attributes::Category, "Prefab")
|
||||||
|
->Event("CreatePrefab", &PrefabSystemScriptingBus::Events::CreatePrefabTemplate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrefabSystemScriptingHandler::Connect(PrefabSystemComponentInterface* prefabSystemComponentInterface)
|
||||||
|
{
|
||||||
|
AZ_Assert(prefabSystemComponentInterface != nullptr, "prefabSystemComponentInterface must not be null");
|
||||||
|
m_prefabSystemComponentInterface = prefabSystemComponentInterface;
|
||||||
|
PrefabSystemScriptingBus::Handler::BusConnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrefabSystemScriptingHandler::Disconnect()
|
||||||
|
{
|
||||||
|
PrefabSystemScriptingBus::Handler::BusDisconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
TemplateId PrefabSystemScriptingHandler::CreatePrefabTemplate(const AZStd::vector<AZ::EntityId>& entityIds, const AZStd::string& filePath)
|
||||||
|
{
|
||||||
|
AZStd::vector<AZ::Entity*> entities;
|
||||||
|
|
||||||
|
for (const auto& entityId : entityIds)
|
||||||
|
{
|
||||||
|
AZ::Entity* entity = nullptr;
|
||||||
|
AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, entityId);
|
||||||
|
|
||||||
|
AZ_Warning(
|
||||||
|
"PrefabSystemComponent", entity, "EntityId %s was not found and will not be added to the prefab",
|
||||||
|
entityId.ToString().c_str());
|
||||||
|
|
||||||
|
if (entity)
|
||||||
|
{
|
||||||
|
entities.push_back(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto prefab = m_prefabSystemComponentInterface->CreatePrefab(entities, {}, AZ::IO::PathView(AZStd::string_view(filePath)));
|
||||||
|
|
||||||
|
if (!prefab)
|
||||||
|
{
|
||||||
|
AZ_Error("PrefabSystemComponenent", false, "Failed to create prefab %s", filePath.c_str());
|
||||||
|
return InvalidTemplateId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return prefab->GetTemplateId();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||||
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <Prefab/PrefabSystemScriptingBus.h>
|
||||||
|
|
||||||
|
namespace AzToolsFramework
|
||||||
|
{
|
||||||
|
namespace Prefab
|
||||||
|
{
|
||||||
|
class PrefabSystemScriptingHandler
|
||||||
|
: PrefabSystemScriptingBus::Handler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void Reflect(AZ::ReflectContext* context);
|
||||||
|
|
||||||
|
PrefabSystemScriptingHandler() = default;
|
||||||
|
|
||||||
|
void Connect(PrefabSystemComponentInterface* prefabSystemComponentInterface);
|
||||||
|
void Disconnect();
|
||||||
|
|
||||||
|
private:
|
||||||
|
AZ_DISABLE_COPY(PrefabSystemScriptingHandler);
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// PrefabSystemScriptingBus implementation
|
||||||
|
TemplateId CreatePrefabTemplate(const AZStd::vector<AZ::EntityId>& entityIds, const AZStd::string& filePath) override;
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
PrefabSystemComponentInterface* m_prefabSystemComponentInterface = nullptr;
|
||||||
|
};
|
||||||
|
} // namespace Prefab
|
||||||
|
} // namespace AzToolsFramework
|
||||||
|
|
||||||
@ -0,0 +1,144 @@
|
|||||||
|
/*
|
||||||
|
* 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 <Prefab/Procedural/ProceduralPrefabAsset.h>
|
||||||
|
#include <AzCore/Serialization/SerializeContext.h>
|
||||||
|
#include <AzCore/Serialization/Json/RegistrationContext.h>
|
||||||
|
#include <AzCore/Settings/SettingsRegistry.h>
|
||||||
|
#include <AzFramework/FileFunc/FileFunc.h>
|
||||||
|
|
||||||
|
namespace AZ::Prefab
|
||||||
|
{
|
||||||
|
static constexpr const char s_useProceduralPrefabsKey[] = "/O3DE/Preferences/Prefabs/UseProceduralPrefabs";
|
||||||
|
|
||||||
|
// ProceduralPrefabAsset
|
||||||
|
|
||||||
|
ProceduralPrefabAsset::ProceduralPrefabAsset(const AZ::Data::AssetId& assetId)
|
||||||
|
: AZ::Data::AssetData(assetId)
|
||||||
|
, m_templateId(AzToolsFramework::Prefab::InvalidTemplateId)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProceduralPrefabAsset::Reflect(AZ::ReflectContext* context)
|
||||||
|
{
|
||||||
|
PrefabDomData::Reflect(context);
|
||||||
|
|
||||||
|
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context); serializeContext != nullptr)
|
||||||
|
{
|
||||||
|
serializeContext->Class<ProceduralPrefabAsset, AZ::Data::AssetData>()
|
||||||
|
->Version(1)
|
||||||
|
->Field("Template Name", &ProceduralPrefabAsset::m_templateName)
|
||||||
|
->Field("Template ID", &ProceduralPrefabAsset::m_templateId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const AZStd::string& ProceduralPrefabAsset::GetTemplateName() const
|
||||||
|
{
|
||||||
|
return m_templateName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProceduralPrefabAsset::SetTemplateName(AZStd::string templateName)
|
||||||
|
{
|
||||||
|
m_templateName = AZStd::move(templateName);
|
||||||
|
}
|
||||||
|
|
||||||
|
AzToolsFramework::Prefab::TemplateId ProceduralPrefabAsset::GetTemplateId() const
|
||||||
|
{
|
||||||
|
return m_templateId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProceduralPrefabAsset::SetTemplateId(AzToolsFramework::Prefab::TemplateId templateId)
|
||||||
|
{
|
||||||
|
m_templateId = templateId;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ProceduralPrefabAsset::UseProceduralPrefabs()
|
||||||
|
{
|
||||||
|
bool useProceduralPrefabs = false;
|
||||||
|
bool result = AZ::SettingsRegistry::Get()->GetObject(useProceduralPrefabs, s_useProceduralPrefabsKey);
|
||||||
|
return result && useProceduralPrefabs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrefabDomData
|
||||||
|
|
||||||
|
void PrefabDomData::Reflect(AZ::ReflectContext* context)
|
||||||
|
{
|
||||||
|
if (auto* jsonContext = azrtti_cast<AZ::JsonRegistrationContext*>(context))
|
||||||
|
{
|
||||||
|
jsonContext->Serializer<PrefabDomDataJsonSerializer>()->HandlesType<PrefabDomData>();
|
||||||
|
}
|
||||||
|
|
||||||
|
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
|
||||||
|
if (serializeContext)
|
||||||
|
{
|
||||||
|
serializeContext->Class<PrefabDomData>()
|
||||||
|
->Version(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrefabDomData::CopyValue(const rapidjson::Value& inputValue)
|
||||||
|
{
|
||||||
|
m_prefabDom.CopyFrom(inputValue, m_prefabDom.GetAllocator());
|
||||||
|
}
|
||||||
|
|
||||||
|
const AzToolsFramework::Prefab::PrefabDom& PrefabDomData::GetValue() const
|
||||||
|
{
|
||||||
|
return m_prefabDom;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrefabDomDataJsonSerializer
|
||||||
|
|
||||||
|
AZ::JsonSerializationResult::Result PrefabDomDataJsonSerializer::Load(
|
||||||
|
void* outputValue,
|
||||||
|
[[maybe_unused]] const AZ::Uuid& outputValueTypeId,
|
||||||
|
const rapidjson::Value& inputValue,
|
||||||
|
AZ::JsonDeserializerContext& context)
|
||||||
|
{
|
||||||
|
AZ_Assert(outputValueTypeId == azrtti_typeid<PrefabDomData>(),
|
||||||
|
"PrefabDomDataJsonSerializer Load against output typeID that was not PrefabDomData");
|
||||||
|
AZ_Assert(outputValue, "PrefabDomDataJsonSerializer Load against null output");
|
||||||
|
|
||||||
|
namespace JSR = AZ::JsonSerializationResult;
|
||||||
|
JSR::ResultCode result(JSR::Tasks::ReadField);
|
||||||
|
|
||||||
|
if (inputValue.IsObject() == false)
|
||||||
|
{
|
||||||
|
result.Combine(context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Missing, "Missing object"));
|
||||||
|
return context.Report(result, "Prefab should be an object.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputValue.MemberCount() < 1)
|
||||||
|
{
|
||||||
|
result.Combine(context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Missing, "Missing members"));
|
||||||
|
return context.Report(result, "Prefab should have multiple members.");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* outputVariable = reinterpret_cast<PrefabDomData*>(outputValue);
|
||||||
|
outputVariable->CopyValue(inputValue);
|
||||||
|
return context.Report(result, "Loaded procedural prefab");
|
||||||
|
}
|
||||||
|
|
||||||
|
AZ::JsonSerializationResult::Result PrefabDomDataJsonSerializer::Store(
|
||||||
|
rapidjson::Value& outputValue,
|
||||||
|
const void* inputValue,
|
||||||
|
[[maybe_unused]] const void* defaultValue,
|
||||||
|
[[maybe_unused]] const AZ::Uuid& valueTypeId,
|
||||||
|
AZ::JsonSerializerContext& context)
|
||||||
|
{
|
||||||
|
AZ_Assert(inputValue, "Input value for PrefabDomDataJsonSerializer can't be null.");
|
||||||
|
AZ_Assert(azrtti_typeid<PrefabDomData>() == valueTypeId,
|
||||||
|
"Unable to Serialize because the provided type is not PrefabGroup::PrefabDomData.");
|
||||||
|
|
||||||
|
const PrefabDomData* prefabDomData = reinterpret_cast<const PrefabDomData*>(inputValue);
|
||||||
|
|
||||||
|
namespace JSR = AZ::JsonSerializationResult;
|
||||||
|
JSR::ResultCode result(JSR::Tasks::WriteValue);
|
||||||
|
outputValue.SetObject();
|
||||||
|
outputValue.CopyFrom(prefabDomData->GetValue(), context.GetJsonAllocator());
|
||||||
|
return context.Report(result, "Stored procedural prefab");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* 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/Asset/AssetCommon.h>
|
||||||
|
#include <AzToolsFramework/Prefab/PrefabDomTypes.h>
|
||||||
|
#include <AzToolsFramework/Prefab/PrefabIdTypes.h>
|
||||||
|
#include <AzCore/Serialization/Json/BaseJsonSerializer.h>
|
||||||
|
|
||||||
|
namespace AZ::Prefab
|
||||||
|
{
|
||||||
|
//! A wrapper around the JSON DOM type so that the assets can read in and write out
|
||||||
|
//! JSON directly since Prefabs are JSON serialized entity-component data
|
||||||
|
class PrefabDomData final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AZ_RTTI(PrefabDomData, "{C73A3360-D772-4D41-9118-A039BF9340C1}");
|
||||||
|
AZ_CLASS_ALLOCATOR(PrefabDomData, AZ::SystemAllocator, 0);
|
||||||
|
|
||||||
|
PrefabDomData() = default;
|
||||||
|
~PrefabDomData() = default;
|
||||||
|
|
||||||
|
static void Reflect(AZ::ReflectContext* context);
|
||||||
|
|
||||||
|
void CopyValue(const rapidjson::Value& inputValue);
|
||||||
|
const AzToolsFramework::Prefab::PrefabDom& GetValue() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
AzToolsFramework::Prefab::PrefabDom m_prefabDom;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Registered to help read/write JSON for the PrefabDomData::m_prefabDom
|
||||||
|
class PrefabDomDataJsonSerializer final
|
||||||
|
: public AZ::BaseJsonSerializer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AZ_RTTI(PrefabDomDataJsonSerializer, "{9FC48652-A00B-4EFA-8FD9-345A8E625439}", BaseJsonSerializer);
|
||||||
|
AZ_CLASS_ALLOCATOR(PrefabDomDataJsonSerializer, AZ::SystemAllocator, 0);
|
||||||
|
|
||||||
|
~PrefabDomDataJsonSerializer() override = default;
|
||||||
|
|
||||||
|
AZ::JsonSerializationResult::Result Load(
|
||||||
|
void* outputValue,
|
||||||
|
const AZ::Uuid& outputValueTypeId,
|
||||||
|
const rapidjson::Value& inputValue,
|
||||||
|
AZ::JsonDeserializerContext& context) override;
|
||||||
|
|
||||||
|
AZ::JsonSerializationResult::Result Store(
|
||||||
|
rapidjson::Value& outputValue,
|
||||||
|
const void* inputValue,
|
||||||
|
const void* defaultValue,
|
||||||
|
const AZ::Uuid& valueTypeId,
|
||||||
|
AZ::JsonSerializerContext& context) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! An asset type to register templates into the Prefab system so that they
|
||||||
|
//! can instantiate like Authored Prefabs
|
||||||
|
class ProceduralPrefabAsset
|
||||||
|
: public AZ::Data::AssetData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AZ_CLASS_ALLOCATOR(ProceduralPrefabAsset, AZ::SystemAllocator, 0);
|
||||||
|
AZ_RTTI(ProceduralPrefabAsset, "{9B7C8459-471E-4EAD-A363-7990CC4065A9}", AZ::Data::AssetData);
|
||||||
|
|
||||||
|
static bool UseProceduralPrefabs();
|
||||||
|
|
||||||
|
ProceduralPrefabAsset(const AZ::Data::AssetId& assetId = AZ::Data::AssetId());
|
||||||
|
~ProceduralPrefabAsset() override = default;
|
||||||
|
ProceduralPrefabAsset(const ProceduralPrefabAsset& rhs) = delete;
|
||||||
|
ProceduralPrefabAsset& operator=(const ProceduralPrefabAsset& rhs) = delete;
|
||||||
|
|
||||||
|
const AZStd::string& GetTemplateName() const;
|
||||||
|
void SetTemplateName(AZStd::string templateName);
|
||||||
|
|
||||||
|
AzToolsFramework::Prefab::TemplateId GetTemplateId() const;
|
||||||
|
void SetTemplateId(AzToolsFramework::Prefab::TemplateId templateId);
|
||||||
|
|
||||||
|
static void Reflect(AZ::ReflectContext* context);
|
||||||
|
|
||||||
|
private:
|
||||||
|
AZStd::string m_templateName;
|
||||||
|
AzToolsFramework::Prefab::TemplateId m_templateId;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* 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 <Prefab/ScriptingPrefabLoader.h>
|
||||||
|
|
||||||
|
namespace AzToolsFramework::Prefab
|
||||||
|
{
|
||||||
|
void ScriptingPrefabLoader::Connect(PrefabLoaderInterface* prefabLoaderInterface)
|
||||||
|
{
|
||||||
|
AZ_Assert(prefabLoaderInterface, "prefabLoaderInterface must not be null");
|
||||||
|
|
||||||
|
m_prefabLoaderInterface = prefabLoaderInterface;
|
||||||
|
PrefabLoaderScriptingBus::Handler::BusConnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptingPrefabLoader::Disconnect()
|
||||||
|
{
|
||||||
|
PrefabLoaderScriptingBus::Handler::BusDisconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
AZ::Outcome<AZStd::string, void> ScriptingPrefabLoader::SaveTemplateToString(TemplateId templateId)
|
||||||
|
{
|
||||||
|
AZStd::string json;
|
||||||
|
|
||||||
|
if (m_prefabLoaderInterface->SaveTemplateToString(templateId, json))
|
||||||
|
{
|
||||||
|
return AZ::Success(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
return AZ::Failure();
|
||||||
|
}
|
||||||
|
} // namespace AzToolsFramework::Prefab
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue