merge from development
Signed-off-by: greerdv <greerdv@amazon.com>monroegm-disable-blank-issue-2
commit
dbba71fe3a
@ -0,0 +1,124 @@
|
||||
#
|
||||
# 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, logging
|
||||
import azlmbr.math
|
||||
import azlmbr.bus
|
||||
from scene_helpers import *
|
||||
|
||||
#
|
||||
# SceneAPI Processor
|
||||
#
|
||||
|
||||
def update_manifest(scene):
|
||||
import uuid
|
||||
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)
|
||||
mesh_name_list.sort(key=lambda node: str.casefold(node.get_path()))
|
||||
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 = []
|
||||
previous_entity_id = azlmbr.entity.InvalidEntityId
|
||||
first_mesh = True
|
||||
|
||||
# Make a list of mesh node paths
|
||||
mesh_path_list = list(map(lambda node: node.get_path(), mesh_name_list))
|
||||
|
||||
# Assume the first mesh is the main mesh
|
||||
main_mesh = mesh_name_list[0]
|
||||
mesh_path = main_mesh.get_path()
|
||||
|
||||
# Create a unique mesh group name using the filename + node name
|
||||
mesh_group_name = '{}_{}'.format(source_filename_only, main_mesh.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 mesh_path_list:
|
||||
if node != mesh_path:
|
||||
scene_manifest.mesh_group_unselect_node(mesh_group, node)
|
||||
|
||||
# Create a LOD rule
|
||||
lod_rule = scene_manifest.mesh_group_add_lod_rule(mesh_group)
|
||||
|
||||
# Loop all the mesh nodes after the first
|
||||
for x in mesh_path_list[1:]:
|
||||
# Add a new LOD level
|
||||
lod = scene_manifest.lod_rule_add_lod(lod_rule)
|
||||
# Select the current mesh for this LOD level
|
||||
scene_manifest.lod_select_node(lod, x)
|
||||
|
||||
# Unselect every other mesh for this LOD level
|
||||
for y in mesh_path_list:
|
||||
if y != x:
|
||||
scene_manifest.lod_unselect_node(lod, y)
|
||||
|
||||
# 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 for Mesh component")
|
||||
|
||||
create_prefab(scene_manifest, source_filename_only, [entity_id])
|
||||
|
||||
# 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()
|
||||
except:
|
||||
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,95 @@
|
||||
"""
|
||||
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 traceback, logging, json
|
||||
from typing import Tuple, List
|
||||
|
||||
import azlmbr.bus
|
||||
from scene_api import scene_data as sceneData
|
||||
from scene_api.scene_data import SceneGraphName
|
||||
|
||||
|
||||
def log_exception_traceback():
|
||||
"""
|
||||
Outputs an exception stacktrace.
|
||||
"""
|
||||
data = traceback.format_exc()
|
||||
logger = logging.getLogger('python')
|
||||
logger.error(data)
|
||||
|
||||
|
||||
def sanitize_name_for_disk(name: str):
|
||||
"""
|
||||
Removes illegal filename characters from a string.
|
||||
|
||||
:param name: String to clean.
|
||||
:return: Name with illegal characters removed.
|
||||
"""
|
||||
return "".join(char for char in name if char not in "|<>:\"/?*\\")
|
||||
|
||||
|
||||
def get_mesh_node_names(scene_graph: sceneData.SceneGraph) -> Tuple[List[SceneGraphName], List[str]]:
|
||||
"""
|
||||
Returns a tuple of all the mesh nodes as well as all the node paths
|
||||
|
||||
:param scene_graph: Scene graph to search
|
||||
:return: Tuple of [Mesh Nodes, All Node Paths]
|
||||
"""
|
||||
import azlmbr.scene as sceneApi
|
||||
import azlmbr.scene.graph
|
||||
|
||||
mesh_data_list = []
|
||||
node = scene_graph.get_root()
|
||||
children = []
|
||||
paths = []
|
||||
|
||||
while node.IsValid():
|
||||
# store children to process after siblings
|
||||
if scene_graph.has_node_child(node):
|
||||
children.append(scene_graph.get_node_child(node))
|
||||
|
||||
node_name = sceneData.SceneGraphName(scene_graph.get_node_name(node))
|
||||
paths.append(node_name.get_path())
|
||||
|
||||
# store any node that has mesh data content
|
||||
node_content = scene_graph.get_node_content(node)
|
||||
if node_content.CastWithTypeName('MeshData'):
|
||||
if scene_graph.is_node_end_point(node) is False:
|
||||
if len(node_name.get_path()):
|
||||
mesh_data_list.append(sceneData.SceneGraphName(scene_graph.get_node_name(node)))
|
||||
|
||||
# advance to next node
|
||||
if scene_graph.has_node_sibling(node):
|
||||
node = scene_graph.get_node_sibling(node)
|
||||
elif children:
|
||||
node = children.pop()
|
||||
else:
|
||||
node = azlmbr.scene.graph.NodeIndex()
|
||||
|
||||
return mesh_data_list, paths
|
||||
|
||||
|
||||
def create_prefab(scene_manifest: sceneData.SceneManifest, prefab_name: str, entities: list) -> None:
|
||||
prefab_filename = prefab_name + ".prefab"
|
||||
created_template_id = azlmbr.prefab.PrefabSystemScriptingBus(azlmbr.bus.Broadcast, "CreatePrefab", entities,
|
||||
prefab_filename)
|
||||
|
||||
if created_template_id is None or 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 is not None and output.IsSuccess():
|
||||
json_string = output.GetValue()
|
||||
uuid = azlmbr.math.Uuid_CreateRandom().ToString()
|
||||
json_result = json.loads(json_string)
|
||||
# Add a PrefabGroup to the manifest and store the JSON on it
|
||||
scene_manifest.add_prefab_group(prefab_name, uuid, json_result)
|
||||
else:
|
||||
raise RuntimeError(
|
||||
"SaveTemplateToString failed for template id {}, prefab {}".format(created_template_id, prefab_filename))
|
||||
@ -0,0 +1,52 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
def DeleteEntity_UnderAnotherPrefab():
|
||||
"""
|
||||
Test description:
|
||||
- Creates an entity.
|
||||
- Creates a prefab out of the above entity.
|
||||
- Focuses on the created prefab and destroys the entity within.
|
||||
Checks that the entity is correctly destroyed.
|
||||
"""
|
||||
|
||||
from editor_python_test_tools.editor_entity_utils import EditorEntity
|
||||
from editor_python_test_tools.prefab_utils import Prefab
|
||||
|
||||
import Prefab.tests.PrefabTestUtils as prefab_test_utils
|
||||
|
||||
prefab_test_utils.open_base_tests_level()
|
||||
|
||||
PREFAB_FILE_NAME = 'some_prefab'
|
||||
|
||||
# Creates a new entity at the root level
|
||||
entity = EditorEntity.create_editor_entity()
|
||||
assert entity.id.IsValid(), "Couldn't create entity."
|
||||
|
||||
# Asserts if prefab creation doesn't succeed
|
||||
child_prefab, child_instance = Prefab.create_prefab([entity], PREFAB_FILE_NAME)
|
||||
child_entity_ids_inside_prefab = child_instance.get_direct_child_entities()
|
||||
assert len(
|
||||
child_entity_ids_inside_prefab) == 1, f"{len(child_entity_ids_inside_prefab)} entities found inside prefab" \
|
||||
f" when there should have been just 1 entity"
|
||||
|
||||
child_entity_inside_prefab = child_entity_ids_inside_prefab[0]
|
||||
child_entity_inside_prefab.focus_on_owning_prefab()
|
||||
|
||||
child_entity_inside_prefab.delete()
|
||||
|
||||
# Wait till prefab propagation finishes before validating entity deletion.
|
||||
azlmbr.legacy.general.idle_wait_frames(1)
|
||||
|
||||
child_entity_ids_inside_prefab = child_instance.get_direct_child_entities()
|
||||
assert len(
|
||||
child_entity_ids_inside_prefab) == 0, f"{len(child_entity_ids_inside_prefab)} entities found inside prefab" \
|
||||
f" when there should have been 0 entities"
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(DeleteEntity_UnderAnotherPrefab)
|
||||
@ -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
|
||||
"""
|
||||
|
||||
def DeleteEntity_UnderLevelPrefab():
|
||||
"""
|
||||
Test description:
|
||||
- Creates an entity.
|
||||
- Destroys the created entity.
|
||||
Checks that the entity is correctly destroyed.
|
||||
"""
|
||||
|
||||
from editor_python_test_tools.editor_entity_utils import EditorEntity
|
||||
import Prefab.tests.PrefabTestUtils as prefab_test_utils
|
||||
|
||||
prefab_test_utils.open_base_tests_level()
|
||||
|
||||
# Creates a new Entity at the root level
|
||||
# Asserts if creation didn't succeed
|
||||
entity = EditorEntity.create_editor_entity_at((100.0, 100.0, 100.0), name = "TestEntity")
|
||||
assert entity.id.IsValid(), "Couldn't create entity"
|
||||
|
||||
level_container_entity = EditorEntity(entity.get_parent_id())
|
||||
entity.delete()
|
||||
|
||||
# Wait till prefab propagation finishes before validating entity deletion.
|
||||
azlmbr.legacy.general.idle_wait_frames(1)
|
||||
level_container_child_entities_count = len(level_container_entity.get_children_ids())
|
||||
assert level_container_child_entities_count == 0, f"The level still has {level_container_child_entities_count}" \
|
||||
f" children when it should have 0."
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(DeleteEntity_UnderLevelPrefab)
|
||||
@ -0,0 +1,20 @@
|
||||
"""
|
||||
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 azlmbr.bus
|
||||
import azlmbr.editor
|
||||
import azlmbr.legacy.general
|
||||
import sys
|
||||
|
||||
# Print out the passed in bundle_path, so the outer test can verify this was sent in correctly
|
||||
bundle_path = sys.argv[1]
|
||||
print('Bundle mode test running with path {}'.format(sys.argv[1]))
|
||||
|
||||
# Turn on bundle mode. This will trigger some printouts that the outer test logic will validate.
|
||||
azlmbr.legacy.general.set_cvar_integer("sys_report_files_not_found_in_paks", 1)
|
||||
azlmbr.legacy.general.run_console(f"loadbundles {bundle_path}")
|
||||
|
||||
azlmbr.editor.EditorToolsApplicationRequestBus(azlmbr.bus.Broadcast, 'ExitNoPrompt')
|
||||
@ -0,0 +1,93 @@
|
||||
"""
|
||||
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
|
||||
import pytest
|
||||
import logging
|
||||
import sys
|
||||
import time
|
||||
pytest.importorskip('ly_test_tools')
|
||||
|
||||
import ly_test_tools.environment.file_system as fs
|
||||
import ly_test_tools.environment.waiter as waiter
|
||||
import ly_test_tools.log.log_monitor
|
||||
|
||||
from ..ap_fixtures.asset_processor_fixture import asset_processor as asset_processor
|
||||
from ..ap_fixtures.bundler_batch_setup_fixture import bundler_batch_setup_fixture as bundler_batch_helper
|
||||
from ..ap_fixtures.timeout_option_fixture import timeout_option_fixture as timeout
|
||||
|
||||
@pytest.mark.SUITE_periodic
|
||||
@pytest.mark.parametrize('launcher_platform', ['windows_editor'])
|
||||
@pytest.mark.parametrize('project', ['AutomatedTesting'])
|
||||
@pytest.mark.parametrize('level', ['auto_test'])
|
||||
class TestBundleMode(object):
|
||||
def test_bundle_mode_with_levels_mounts_bundles_correctly(self, request, editor, level, launcher_platform,
|
||||
asset_processor, workspace, bundler_batch_helper):
|
||||
level_pak = os.path.join("levels", level, "level.pak")
|
||||
|
||||
bundles_folder = os.path.join(workspace.paths.project(), "Bundles")
|
||||
bundle_request_path = os.path.join(bundles_folder, "bundle.pak")
|
||||
bundle_result_path = os.path.join(bundles_folder,
|
||||
bundler_batch_helper.platform_file_name(
|
||||
"bundle.pak", workspace.asset_processor_platform))
|
||||
|
||||
# Create target 'Bundles' folder if it doesn't exist
|
||||
if not os.path.exists(bundles_folder):
|
||||
os.mkdir(bundles_folder)
|
||||
# Delete target bundle file if it already exists
|
||||
if os.path.exists(bundle_result_path):
|
||||
fs.delete([bundle_result_path], True, False)
|
||||
|
||||
# Make asset list file to use in the bundle
|
||||
bundler_batch_helper.call_assetLists(
|
||||
addSeed=level_pak,
|
||||
assetListFile=bundler_batch_helper["asset_info_file_request"],
|
||||
)
|
||||
|
||||
# Make bundle in <project_folder>/Bundles
|
||||
bundler_batch_helper.call_bundles(
|
||||
assetListFile=bundler_batch_helper["asset_info_file_result"],
|
||||
outputBundlePath=bundle_request_path,
|
||||
maxSize="2048",
|
||||
)
|
||||
|
||||
# Ensure the bundle was created
|
||||
assert os.path.exists(bundle_result_path), f"Bundle was not created at location: {bundle_result_path}"
|
||||
|
||||
# The editor flips the slash direction in some of the printouts
|
||||
bundle_result_path_editor_separator = bundle_result_path.replace('\\', '/')
|
||||
|
||||
expected_lines = [
|
||||
# A beginning of test printout can help debug where failures occur, if this line is missing
|
||||
# then the Editor didn't launch, didn't run the Python test, or didn't pass in the right parameter
|
||||
f'Bundle mode test running with path {bundles_folder}',
|
||||
# These printouts happen in response to the loadbundles call, and verify this bundle is actually loaded
|
||||
f"[CONSOLE] Executing console command 'loadbundles {bundles_folder}'",
|
||||
f'(BundlingSystem) - Loading bundles from {bundles_folder} of type .pak',
|
||||
f'(Archive) - Opening archive file {bundle_result_path_editor_separator}',
|
||||
]
|
||||
unexpected_lines = []
|
||||
|
||||
timeout = 180
|
||||
halt_on_unexpected = False
|
||||
test_directory = os.path.join(os.path.dirname(__file__))
|
||||
test_file = os.path.join(test_directory, 'bundle_mode_in_editor_tests.py')
|
||||
editor.args.extend(['-NullRenderer', '-rhi=Null', "--skipWelcomeScreenDialog",
|
||||
"--autotest_mode", "--runpythontest", test_file, "--runpythonargs", bundles_folder])
|
||||
|
||||
with editor.start(launch_ap=True):
|
||||
editor_log_file = os.path.join(editor.workspace.paths.project_log(), 'Editor.log')
|
||||
log_monitor = ly_test_tools.log.log_monitor.LogMonitor(editor, editor_log_file)
|
||||
waiter.wait_for(
|
||||
lambda: editor.is_alive(),
|
||||
timeout,
|
||||
exc=("Log file '{}' was never opened by another process.".format(editor_log_file)),
|
||||
interval=1)
|
||||
log_monitor.monitor_log_for_lines(expected_lines, unexpected_lines, halt_on_unexpected, timeout)
|
||||
|
||||
# Delete the bundle created and used in this test
|
||||
fs.delete([bundle_result_path], True, False)
|
||||
@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7e169277bca473325281d5fe043cffc9196bd3ef46f6bffbea6e0b5e3b7194a1
|
||||
size 62700
|
||||
@ -0,0 +1,8 @@
|
||||
{
|
||||
"values": [
|
||||
{
|
||||
"$type": "ScriptProcessorRule",
|
||||
"scriptFilename": "Editor/Scripts/auto_lod.py"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -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/Component/EntityId.h>
|
||||
#include <AzCore/EBus/EBus.h>
|
||||
|
||||
#include <AzFramework/Entity/EntityContext.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
//! Used to notify changes of state for read-only entities.
|
||||
class ReadOnlyEntityPublicNotifications
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// EBusTraits overrides
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
|
||||
using BusIdType = AzFramework::EntityContextId;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//! Triggered when an entity's read-only status changes.
|
||||
//! @param entityId The entity whose status has changed.
|
||||
//! @param readOnly The read-only state the container was changed to.
|
||||
virtual void OnReadOnlyEntityStatusChanged([[maybe_unused]] const AZ::EntityId& entityId, [[maybe_unused]] bool readOnly) {}
|
||||
|
||||
protected:
|
||||
~ReadOnlyEntityPublicNotifications() = default;
|
||||
};
|
||||
using ReadOnlyEntityPublicNotificationBus = AZ::EBus<ReadOnlyEntityPublicNotifications>;
|
||||
|
||||
//! Used by the ReadOnlyEntitySystemComponent to query the read-only state of entities as set by systems using the API.
|
||||
class ReadOnlyEntityQueryRequests
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// EBusTraits overrides
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
|
||||
using BusIdType = AzFramework::EntityContextId;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//! Triggered when an entity's read-only status is queried.
|
||||
//! Allows multiple systems to weigh in on the read-only status of an entity.
|
||||
//! @param entityId The entity whose status has changed.
|
||||
//! @param[out] isReadOnly The output of the query. Should only be changed to true, and left untouched if false.
|
||||
virtual void IsReadOnly(const AZ::EntityId& entityId, bool& isReadOnly) = 0;
|
||||
|
||||
protected:
|
||||
~ReadOnlyEntityQueryRequests() = default;
|
||||
};
|
||||
using ReadOnlyEntityQueryRequestBus = AZ::EBus<ReadOnlyEntityQueryRequests>;
|
||||
|
||||
} // namespace AzToolsFramework
|
||||
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
#include <AzFramework/Entity/EntityContextBus.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
//! An entity registered as read-only cannot be altered in the editor.
|
||||
class ReadOnlyEntityPublicInterface
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(ReadOnlyEntityPublicInterface, "{921FE15B-6EBD-47F0-8238-BC63318DEDEA}");
|
||||
|
||||
//! Returns whether the entity id provided is registered as read-only.
|
||||
virtual bool IsReadOnly(const AZ::EntityId& entityId) = 0;
|
||||
};
|
||||
|
||||
//! An entity registered as read-only cannot be altered in the editor.
|
||||
class ReadOnlyEntityQueryInterface
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(ReadOnlyEntityQueryInterface, "{2ACD63C5-1F3E-4DE8-880E-8115F857D329}");
|
||||
|
||||
//! Refreshes the cached read-only status for the entities provided.
|
||||
//! @param entityIds The entityIds whose read-only state will be queried again.
|
||||
virtual void RefreshReadOnlyState(const EntityIdList& entityIds) = 0;
|
||||
|
||||
//! Refreshes the cached read-only status for all entities.
|
||||
//! Useful when disconnecting a handler at runtime.
|
||||
virtual void RefreshReadOnlyStateForAllEntities() = 0;
|
||||
};
|
||||
|
||||
} // namespace AzToolsFramework
|
||||
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzToolsFramework/Entity/ReadOnly/ReadOnlyEntitySystemComponent.h>
|
||||
|
||||
#include <AzToolsFramework/Entity/ReadOnly/ReadOnlyEntityBus.h>
|
||||
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
void ReadOnlyEntitySystemComponent::Activate()
|
||||
{
|
||||
AZ::Interface<ReadOnlyEntityQueryInterface>::Register(this);
|
||||
AZ::Interface<ReadOnlyEntityPublicInterface>::Register(this);
|
||||
EditorEntityContextNotificationBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
void ReadOnlyEntitySystemComponent::Deactivate()
|
||||
{
|
||||
EditorEntityContextNotificationBus::Handler::BusDisconnect();
|
||||
AZ::Interface<ReadOnlyEntityPublicInterface>::Unregister(this);
|
||||
AZ::Interface<ReadOnlyEntityQueryInterface>::Unregister(this);
|
||||
}
|
||||
|
||||
void ReadOnlyEntitySystemComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<ReadOnlyEntitySystemComponent, AZ::Component>()->Version(1);
|
||||
}
|
||||
}
|
||||
|
||||
void ReadOnlyEntitySystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.push_back(AZ_CRC_CE("ReadOnlyEntityService"));
|
||||
}
|
||||
|
||||
bool ReadOnlyEntitySystemComponent::IsReadOnly(const AZ::EntityId& entityId)
|
||||
{
|
||||
if (!m_readOnlystates.contains(entityId))
|
||||
{
|
||||
QueryReadOnlyStateForEntity(entityId);
|
||||
}
|
||||
|
||||
return m_readOnlystates[entityId];
|
||||
}
|
||||
|
||||
void ReadOnlyEntitySystemComponent::RefreshReadOnlyState(const EntityIdList& entityIds)
|
||||
{
|
||||
for (const AZ::EntityId entityId : entityIds)
|
||||
{
|
||||
bool wasReadOnly = m_readOnlystates[entityId];
|
||||
QueryReadOnlyStateForEntity(entityId);
|
||||
|
||||
if (bool isReadOnly = m_readOnlystates[entityId]; wasReadOnly != isReadOnly)
|
||||
{
|
||||
ReadOnlyEntityPublicNotificationBus::Broadcast(
|
||||
&ReadOnlyEntityPublicNotificationBus::Events::OnReadOnlyEntityStatusChanged, entityId, isReadOnly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReadOnlyEntitySystemComponent::RefreshReadOnlyStateForAllEntities()
|
||||
{
|
||||
for (auto elem : m_readOnlystates)
|
||||
{
|
||||
AZ::EntityId entityId = elem.first;
|
||||
bool wasReadOnly = m_readOnlystates[entityId];
|
||||
QueryReadOnlyStateForEntity(entityId);
|
||||
|
||||
if (bool isReadOnly = m_readOnlystates[entityId]; wasReadOnly != isReadOnly)
|
||||
{
|
||||
ReadOnlyEntityPublicNotificationBus::Broadcast(
|
||||
&ReadOnlyEntityPublicNotificationBus::Events::OnReadOnlyEntityStatusChanged, entityId, isReadOnly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReadOnlyEntitySystemComponent::OnContextReset()
|
||||
{
|
||||
m_readOnlystates.clear();
|
||||
}
|
||||
|
||||
void ReadOnlyEntitySystemComponent::QueryReadOnlyStateForEntity(const AZ::EntityId& entityId)
|
||||
{
|
||||
bool isReadOnly = false;
|
||||
|
||||
ReadOnlyEntityQueryRequestBus::Broadcast(
|
||||
&ReadOnlyEntityQueryRequestBus::Events::IsReadOnly, entityId, isReadOnly);
|
||||
|
||||
m_readOnlystates[entityId] = isReadOnly;
|
||||
}
|
||||
|
||||
} // namespace AzToolsFramework
|
||||
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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/Memory/SystemAllocator.h>
|
||||
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
#include <AzToolsFramework/Entity/ReadOnly/ReadOnlyEntityInterface.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
//! System Component to track read-only entity registration.
|
||||
//! An entity registered as ReadOnly cannot be altered in the Editor.
|
||||
class ReadOnlyEntitySystemComponent final
|
||||
: public AZ::Component
|
||||
, private ReadOnlyEntityPublicInterface
|
||||
, private ReadOnlyEntityQueryInterface
|
||||
, private EditorEntityContextNotificationBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(ReadOnlyEntitySystemComponent, "{B32EB03F-D88F-4B3A-9C16-071AF04DA646}");
|
||||
|
||||
ReadOnlyEntitySystemComponent() = default;
|
||||
virtual ~ReadOnlyEntitySystemComponent() = default;
|
||||
|
||||
// AZ::Component overrides ...
|
||||
void Activate() override;
|
||||
void Deactivate() override;
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
|
||||
|
||||
// ReadOnlyEntityPublicNotifications overrides ...
|
||||
bool IsReadOnly(const AZ::EntityId& entityId) override;
|
||||
|
||||
// ReadOnlyEntityQueryInterface overrides ...
|
||||
void RefreshReadOnlyState(const EntityIdList& entityIds) override;
|
||||
void RefreshReadOnlyStateForAllEntities() override;
|
||||
|
||||
// EditorEntityContextNotificationBus overrides ...
|
||||
void OnContextReset() override;
|
||||
|
||||
private:
|
||||
void QueryReadOnlyStateForEntity(const AZ::EntityId& entityId);
|
||||
|
||||
AZStd::unordered_map<AZ::EntityId, bool> m_readOnlystates;
|
||||
};
|
||||
|
||||
} // namespace AzToolsFramework
|
||||
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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 <Tests/Entity/ReadOnly/ReadOnlyEntityFixture.h>
|
||||
|
||||
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
void ReadOnlyEntityFixture::SetUpEditorFixtureImpl()
|
||||
{
|
||||
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
|
||||
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
|
||||
// in the unit tests.
|
||||
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
|
||||
|
||||
m_readOnlyEntityPublicInterface = AZ::Interface<ReadOnlyEntityPublicInterface>::Get();
|
||||
ASSERT_TRUE(m_readOnlyEntityPublicInterface != nullptr);
|
||||
|
||||
GenerateTestHierarchy();
|
||||
}
|
||||
|
||||
void ReadOnlyEntityFixture::TearDownEditorFixtureImpl()
|
||||
{
|
||||
}
|
||||
|
||||
void ReadOnlyEntityFixture::GenerateTestHierarchy()
|
||||
{
|
||||
/*
|
||||
* Root
|
||||
* |_ Child
|
||||
* |_ GrandChild1
|
||||
* |_ GrandChild2
|
||||
*/
|
||||
|
||||
m_entityMap[RootEntityName] = CreateEditorEntity(RootEntityName, AZ::EntityId());
|
||||
m_entityMap[ChildEntityName] = CreateEditorEntity(ChildEntityName, m_entityMap[RootEntityName]);
|
||||
m_entityMap[GrandChild1EntityName] = CreateEditorEntity(GrandChild1EntityName, m_entityMap[ChildEntityName]);
|
||||
m_entityMap[GrandChild2EntityName] = CreateEditorEntity(GrandChild2EntityName, m_entityMap[ChildEntityName]);
|
||||
}
|
||||
|
||||
AZ::EntityId ReadOnlyEntityFixture::CreateEditorEntity(const char* name, AZ::EntityId parentId)
|
||||
{
|
||||
AZ::Entity* entity = nullptr;
|
||||
UnitTest::CreateDefaultEditorEntity(name, &entity);
|
||||
|
||||
// Parent
|
||||
AZ::TransformBus::Event(entity->GetId(), &AZ::TransformInterface::SetParent, parentId);
|
||||
|
||||
return entity->GetId();
|
||||
}
|
||||
|
||||
ReadOnlyHandlerAlwaysTrue::ReadOnlyHandlerAlwaysTrue()
|
||||
{
|
||||
auto editorEntityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
|
||||
|
||||
ReadOnlyEntityQueryRequestBus::Handler::BusConnect(editorEntityContextId);
|
||||
}
|
||||
|
||||
ReadOnlyHandlerAlwaysTrue::~ReadOnlyHandlerAlwaysTrue()
|
||||
{
|
||||
ReadOnlyEntityQueryRequestBus::Handler::BusDisconnect();
|
||||
|
||||
if (auto readOnlyEntityQueryInterface = AZ::Interface<ReadOnlyEntityQueryInterface>::Get())
|
||||
{
|
||||
readOnlyEntityQueryInterface->RefreshReadOnlyStateForAllEntities();
|
||||
}
|
||||
}
|
||||
|
||||
void ReadOnlyHandlerAlwaysTrue::IsReadOnly([[maybe_unused]] const AZ::EntityId& entityId, bool& isReadOnly)
|
||||
{
|
||||
isReadOnly = true;
|
||||
}
|
||||
|
||||
ReadOnlyHandlerAlwaysFalse::ReadOnlyHandlerAlwaysFalse()
|
||||
{
|
||||
auto editorEntityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
|
||||
|
||||
ReadOnlyEntityQueryRequestBus::Handler::BusConnect(editorEntityContextId);
|
||||
}
|
||||
|
||||
ReadOnlyHandlerAlwaysFalse::~ReadOnlyHandlerAlwaysFalse()
|
||||
{
|
||||
ReadOnlyEntityQueryRequestBus::Handler::BusDisconnect();
|
||||
|
||||
if (auto readOnlyEntityQueryInterface = AZ::Interface<ReadOnlyEntityQueryInterface>::Get())
|
||||
{
|
||||
readOnlyEntityQueryInterface->RefreshReadOnlyStateForAllEntities();
|
||||
}
|
||||
}
|
||||
|
||||
ReadOnlyHandlerEntityId::ReadOnlyHandlerEntityId(AZ::EntityId entityId)
|
||||
: m_entityId(entityId)
|
||||
{
|
||||
auto editorEntityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
|
||||
|
||||
ReadOnlyEntityQueryRequestBus::Handler::BusConnect(editorEntityContextId);
|
||||
}
|
||||
|
||||
ReadOnlyHandlerEntityId::~ReadOnlyHandlerEntityId()
|
||||
{
|
||||
ReadOnlyEntityQueryRequestBus::Handler::BusDisconnect();
|
||||
|
||||
if (auto readOnlyEntityQueryInterface = AZ::Interface<ReadOnlyEntityQueryInterface>::Get())
|
||||
{
|
||||
readOnlyEntityQueryInterface->RefreshReadOnlyStateForAllEntities();
|
||||
}
|
||||
}
|
||||
|
||||
void ReadOnlyHandlerEntityId::IsReadOnly(const AZ::EntityId& entityId, bool& isReadOnly)
|
||||
{
|
||||
if (entityId == m_entityId)
|
||||
{
|
||||
isReadOnly = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
|
||||
#include <AzTest/AzTest.h>
|
||||
|
||||
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
|
||||
#include <AzToolsFramework/Entity/ReadOnly/ReadOnlyEntityBus.h>
|
||||
#include <AzToolsFramework/Entity/ReadOnly/ReadOnlyEntityInterface.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
class ReadOnlyEntityFixture
|
||||
: public UnitTest::ToolsApplicationFixture
|
||||
{
|
||||
protected:
|
||||
void SetUpEditorFixtureImpl() override;
|
||||
void TearDownEditorFixtureImpl() override;
|
||||
|
||||
void GenerateTestHierarchy();
|
||||
AZ::EntityId CreateEditorEntity(const char* name, AZ::EntityId parentId);
|
||||
|
||||
AZStd::unordered_map<AZStd::string, AZ::EntityId> m_entityMap;
|
||||
|
||||
ReadOnlyEntityPublicInterface* m_readOnlyEntityPublicInterface = nullptr;
|
||||
|
||||
public:
|
||||
inline static const char* RootEntityName = "Root";
|
||||
inline static const char* ChildEntityName = "Child";
|
||||
inline static const char* GrandChild1EntityName = "GrandChild1";
|
||||
inline static const char* GrandChild2EntityName = "GrandChild2";
|
||||
};
|
||||
|
||||
class ReadOnlyHandlerAlwaysTrue
|
||||
: public ReadOnlyEntityQueryRequestBus::Handler
|
||||
{
|
||||
public:
|
||||
ReadOnlyHandlerAlwaysTrue();
|
||||
~ReadOnlyHandlerAlwaysTrue();
|
||||
|
||||
// ReadOnlyEntityQueryNotificationBus overrides ...
|
||||
void IsReadOnly(const AZ::EntityId& entityId, bool& isReadOnly) override;
|
||||
};
|
||||
|
||||
class ReadOnlyHandlerAlwaysFalse
|
||||
: public ReadOnlyEntityQueryRequestBus::Handler
|
||||
{
|
||||
public:
|
||||
ReadOnlyHandlerAlwaysFalse();
|
||||
~ReadOnlyHandlerAlwaysFalse();
|
||||
|
||||
// ReadOnlyEntityQueryNotificationBus overrides ...
|
||||
void IsReadOnly([[maybe_unused]] const AZ::EntityId& entityId, [[maybe_unused]] bool& isReadOnly) override {}
|
||||
};
|
||||
|
||||
class ReadOnlyHandlerEntityId
|
||||
: public ReadOnlyEntityQueryRequestBus::Handler
|
||||
{
|
||||
public:
|
||||
ReadOnlyHandlerEntityId(AZ::EntityId entityId);
|
||||
~ReadOnlyHandlerEntityId();
|
||||
|
||||
// ReadOnlyEntityQueryNotificationBus overrides ...
|
||||
void IsReadOnly(const AZ::EntityId& entityId, bool& isReadOnly) override;
|
||||
|
||||
private:
|
||||
AZ::EntityId m_entityId;
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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 <Tests/Entity/ReadOnly/ReadOnlyEntityFixture.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
TEST_F(ReadOnlyEntityFixture, NoHandlerEntityIsNotReadOnlyByDefault)
|
||||
{
|
||||
EXPECT_FALSE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[ChildEntityName]));
|
||||
}
|
||||
|
||||
TEST_F(ReadOnlyEntityFixture, SingleHandlerEntityIsReadOnly)
|
||||
{
|
||||
// Create a handler that sets all entities to read-only.
|
||||
ReadOnlyHandlerAlwaysTrue alwaysTrueHandler;
|
||||
|
||||
// All entities should be marked read-only now.
|
||||
EXPECT_TRUE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[RootEntityName]));
|
||||
EXPECT_TRUE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[ChildEntityName]));
|
||||
EXPECT_TRUE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[GrandChild1EntityName]));
|
||||
EXPECT_TRUE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[GrandChild2EntityName]));
|
||||
}
|
||||
|
||||
TEST_F(ReadOnlyEntityFixture, SingleHandlerEntityIsNotReadOnly)
|
||||
{
|
||||
// Create a handler that sets all entities to read-only.
|
||||
ReadOnlyHandlerAlwaysFalse alwaysFalseHandler;
|
||||
|
||||
// All entities should not be marked read-only now.
|
||||
EXPECT_FALSE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[RootEntityName]));
|
||||
EXPECT_FALSE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[ChildEntityName]));
|
||||
EXPECT_FALSE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[GrandChild1EntityName]));
|
||||
EXPECT_FALSE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[GrandChild2EntityName]));
|
||||
}
|
||||
|
||||
TEST_F(ReadOnlyEntityFixture, SingleHandlerWithLogic)
|
||||
{
|
||||
// Create a handler that sets just the child entity to read-only.
|
||||
ReadOnlyHandlerEntityId entityIdHandler(m_entityMap[ChildEntityName]);
|
||||
|
||||
EXPECT_FALSE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[RootEntityName]));
|
||||
EXPECT_TRUE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[ChildEntityName]));
|
||||
EXPECT_FALSE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[GrandChild1EntityName]));
|
||||
EXPECT_FALSE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[GrandChild2EntityName]));
|
||||
}
|
||||
|
||||
TEST_F(ReadOnlyEntityFixture, TwoHandlersCanOverlap)
|
||||
{
|
||||
// Create two handlers that set different entities to read-only.
|
||||
ReadOnlyHandlerEntityId entityIdHandler1(m_entityMap[ChildEntityName]);
|
||||
ReadOnlyHandlerEntityId entityIdHandler2(m_entityMap[GrandChild2EntityName]);
|
||||
|
||||
// Both entities should be marked as read-only, while others aren't.
|
||||
EXPECT_FALSE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[RootEntityName]));
|
||||
EXPECT_TRUE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[ChildEntityName]));
|
||||
EXPECT_FALSE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[GrandChild1EntityName]));
|
||||
EXPECT_TRUE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[GrandChild2EntityName]));
|
||||
}
|
||||
|
||||
TEST_F(ReadOnlyEntityFixture, EnsureCacheIsRefreshedCorrectly)
|
||||
{
|
||||
// Verify the child entity is not marked as read-only
|
||||
EXPECT_FALSE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[ChildEntityName]));
|
||||
|
||||
// Create a handler that sets the child entity to read-only.
|
||||
ReadOnlyHandlerEntityId entityIdHandler(m_entityMap[ChildEntityName]);
|
||||
|
||||
// Communicate to the ReadOnlyEntitySystemComponent that the read-only state for the child entity may have changed.
|
||||
// Note that this operation would usually be executed by the handler, hence the Query interface call.
|
||||
if (auto readOnlyEntityQueryInterface = AZ::Interface<ReadOnlyEntityQueryInterface>::Get())
|
||||
{
|
||||
readOnlyEntityQueryInterface->RefreshReadOnlyState({ m_entityMap[ChildEntityName] });
|
||||
}
|
||||
|
||||
// Verify the child entity is marked as read-only
|
||||
EXPECT_TRUE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[ChildEntityName]));
|
||||
}
|
||||
|
||||
TEST_F(ReadOnlyEntityFixture, EnsureCacheIsClearedCorrectly)
|
||||
{
|
||||
{
|
||||
// Create a handler that sets the child entity to read-only.
|
||||
ReadOnlyHandlerEntityId entityIdHandler(m_entityMap[ChildEntityName]);
|
||||
|
||||
// Verify the child entity is marked as read-only
|
||||
EXPECT_TRUE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[ChildEntityName]));
|
||||
}
|
||||
// When the handler goes out of scope, it calls RefreshReadOnlyStateForAllEntities and refreshes the cache.
|
||||
|
||||
// Verify the child entity is no longer marked as read-only
|
||||
EXPECT_FALSE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[ChildEntityName]));
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue