Merge branch 'stabilization/2106' into Helios_TransformImporterFixV2_stabilization_2106

This commit is contained in:
amzn-mike
2021-06-21 08:16:21 -05:00
390 changed files with 32081 additions and 18885 deletions
-34
View File
@@ -1,34 +0,0 @@
CryEngine tips of the day
You can toggle snap to grid by pressing G.
Ctrl+Shift+Clicking somewhere with an object selected quickly moves the object to that position when in move mode.
Pressing M will open the material editor.
Show and Hide helpers is bound to Shift + Space by default.
Enable AI/Physics is bound to Ctrl + P by default.
You can save a viewport location by pressing Ctrl + F1 through f12 and go to that position using Shift + F1 through F12.
You can link objects together by using the link command on the top menu of the editor.
Pressing 1 through 5 on the keyboard will cycle through brush operations such as move or scale.
You can simply bind keyboard shortcuts to editor functions by going to Tools --> Customize Keyboard.
Pressing H will hide the selected objects, Ctrl-H will unhide all hidden objects.
Pressing F will freeze the selected objects, Ctrl-F will unfreeze all frozen objects.
Pressing F3 will toggle wireframe view.
Camera/terrain collision can be toggled using Q.
You can restart the Editor by pressing the restart button on your PC.
Pressing Ctrl-C with an object selected will clone that object.
Toggle the console by pressing the tilde (~) key.
You can dock windows by dragging them onto the blue helpers that appear when you grab a window by the titlebar.
You can select materials by clicking on the dropper icon in the material editor and then clicking on the material you wish to select.
You can right click on the previewer in the material editor and change the model to different shapes and background colors.
Materials can be saved in the local level folder for re-distribution.
Always keep your level free of errors and immidiately fix errors reported by the error report screen when you load your level.
You must always export to engine before you can run it in pure game mode. (File --> Export to engine)
You must re-triangulate AI before playing your level in game mode. (AI --> Generate all navigation)
You must always re-generate surface textures after you finish painting the terrain. (File --> Regenerate surface textures)
Press Ctrl-G or F12 to go into the Game mode, ESC to return to Editing mode.
Quickly rebuild a level (without regenerating the ground texture) by pressing Ctrl-E.
Hold down the third mouse button and drag to move the camera up and down.
Missing objects are represented by a bright yellow sphere.
Hold Alt + Middle Mouse button to rotate around an object.
Select multiple objects by holding Ctrl.
You can place multiple instances of vegetation by holding Shift and clicking on the terrain.
A number of useful commands can be found in Tools --> User commands. This can also be dragged and docked to the main window.
+3
View File
@@ -15,6 +15,8 @@ include_guard()
# Read the engine name from the project_json file
file(READ ${CMAKE_CURRENT_LIST_DIR}/project.json project_json)
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/project.json)
string(JSON LY_ENGINE_NAME_TO_USE ERROR_VARIABLE json_error GET ${project_json} engine)
if(json_error)
message(FATAL_ERROR "Unable to read key 'engine' from 'project.json', error: ${json_error}")
@@ -30,6 +32,7 @@ endif()
# Find a key that matches LY_ENGINE_NAME_TO_USE and use that as the engine path.
if(EXISTS ${manifest_path})
file(READ ${manifest_path} manifest_json)
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${manifest_path})
string(JSON engines_path_count ERROR_VARIABLE json_error LENGTH ${manifest_json} engines_path)
if(json_error)
@@ -21,6 +21,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
TEST_SUITE periodic
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/${PAL_PLATFORM_NAME}/
TIMEOUT 3000
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
@@ -18,11 +18,12 @@ import typing
from datetime import datetime
import ly_test_tools.log.log_monitor
from assetpipeline.ap_fixtures.asset_processor_fixture import asset_processor as asset_processor
from AWS.common.aws_utils import aws_utils
from AWS.common.aws_credentials import aws_credentials
# fixture imports
from AWS.Windows.resource_mappings.resource_mappings import resource_mappings
from AWS.Windows.cdk.cdk import cdk
from AWS.Windows.cdk.cdk_utils import Cdk
from AWS.common.aws_utils import AwsUtils
from assetpipeline.ap_fixtures.asset_processor_fixture import asset_processor as asset_processor
from AWS.common.aws_credentials import aws_credentials
from .aws_metrics_utils import aws_metrics_utils
AWS_METRICS_FEATURE_NAME = 'AWSMetrics'
@@ -32,7 +33,7 @@ logger = logging.getLogger(__name__)
def setup(launcher: ly_test_tools.launchers.Launcher,
cdk: cdk,
cdk: Cdk,
asset_processor: asset_processor,
resource_mappings: resource_mappings,
context_variable: str = '') -> typing.Tuple[ly_test_tools.log.log_monitor.LogMonitor, str, str]:
@@ -116,18 +117,18 @@ def remove_file(file_path: str) -> None:
@pytest.mark.parametrize('region_name', ['us-west-2'])
@pytest.mark.parametrize('assume_role_arn', ['arn:aws:iam::645075835648:role/o3de-automation-tests'])
@pytest.mark.parametrize('session_name', ['o3de-Automation-session'])
class TestAWSMetrics_Windows(object):
def test_AWSMetrics_RealTimeAnalytics_MetricsSentToCloudWatch(self,
level: str,
launcher: ly_test_tools.launchers.Launcher,
asset_processor: pytest.fixture,
workspace: pytest.fixture,
aws_utils: aws_utils,
aws_credentials: aws_credentials,
resource_mappings: resource_mappings,
cdk: cdk,
aws_metrics_utils: aws_metrics_utils,
):
class TestAWSMetricsWindows(object):
def test_realtime_analytics_metrics_sent_to_cloudwatch(self,
level: str,
launcher: ly_test_tools.launchers.Launcher,
asset_processor: pytest.fixture,
workspace: pytest.fixture,
aws_utils: pytest.fixture,
aws_credentials: aws_credentials,
resource_mappings: pytest.fixture,
cdk: pytest.fixture,
aws_metrics_utils: aws_metrics_utils,
):
"""
Tests that the submitted metrics are sent to CloudWatch for real-time analytics.
"""
@@ -148,7 +149,7 @@ class TestAWSMetrics_Windows(object):
'AWS/Lambda',
'Invocations',
[{'Name': 'FunctionName',
'Value': f'{stack_name}-AnalyticsProcessingLambda'}],
'Value': f'{stack_name}-AnalyticsProcessingLambdaName'}],
start_time)
logger.info('Operational health metrics sent to CloudWatch.')
@@ -162,14 +163,14 @@ class TestAWSMetrics_Windows(object):
# Stop the Kinesis Data Analytics application.
aws_metrics_utils.stop_kinesis_data_analytics_application(analytics_application_name)
def test_AWSMetrics_UnauthorizedUser_RequestRejected(self,
level: str,
launcher: ly_test_tools.launchers.Launcher,
cdk: cdk,
aws_credentials: aws_credentials,
asset_processor: pytest.fixture,
resource_mappings: resource_mappings,
workspace: pytest.fixture):
def test_unauthorized_user_request_rejected(self,
level: str,
launcher: ly_test_tools.launchers.Launcher,
cdk: pytest.fixture,
aws_credentials: aws_credentials,
asset_processor: pytest.fixture,
resource_mappings: pytest.fixture,
workspace: pytest.fixture):
"""
Tests that unauthorized users cannot send metrics events to the AWS backed backend.
"""
@@ -187,14 +188,14 @@ class TestAWSMetrics_Windows(object):
assert result, 'Metrics events are sent successfully by unauthorized user'
logger.info('Unauthorized user is rejected to send metrics.')
def test_AWSMetrics_BatchAnalytics_MetricsDeliveredToS3(self,
def test_batch_analytics_metrics_delivered_to_s3(self,
level: str,
launcher: ly_test_tools.launchers.Launcher,
cdk: cdk,
cdk: pytest.fixture,
aws_credentials: aws_credentials,
asset_processor: pytest.fixture,
resource_mappings: resource_mappings,
aws_utils: aws_utils,
resource_mappings: pytest.fixture,
aws_utils: pytest.fixture,
aws_metrics_utils: aws_metrics_utils,
workspace: pytest.fixture):
"""
@@ -234,4 +235,3 @@ class TestAWSMetrics_Windows(object):
time.sleep(60)
# Empty the S3 bucket. S3 buckets can only be deleted successfully when it doesn't contain any object.
aws_metrics_utils.empty_s3_bucket(analytics_bucket_name)
@@ -12,6 +12,10 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
import os
import pytest
import boto3
import uuid
import logging
import subprocess
import botocore
import ly_test_tools.environment.process_utils as process_utils
from typing import List
@@ -19,22 +23,71 @@ from typing import List
BOOTSTRAP_STACK_NAME = 'CDKToolkit'
BOOTSTRAP_STAGING_BUCKET_LOGIC_ID = 'StagingBucket'
logger = logging.getLogger(__name__)
class Cdk:
"""
Cdk class that provides methods to run cdk application commands.
Expects system to have NodeJS, AWS CLI and CDK installed globally and have their paths setup as env variables.
"""
def __init__(self, cdk_path: str, project: str, account_id: str,
workspace: pytest.fixture, session: boto3.session.Session):
def __init__(self):
self._cdk_env = ''
self._stacks = []
self._cdk_path = os.path.dirname(os.path.realpath(__file__))
self._session = ''
cdk_npm_latest_version_cmd = ['npm', 'view', 'aws-cdk', 'version']
output = process_utils.check_output(
cdk_npm_latest_version_cmd,
cwd=self._cdk_path,
shell=True)
cdk_npm_latest_version = output.split()[0]
cdk_version_cmd = ['cdk', 'version']
output = process_utils.check_output(
cdk_version_cmd,
cwd=self._cdk_path,
shell=True)
cdk_version = output.split()[0]
logger.info(f'Current CDK version {cdk_version}')
if cdk_version != cdk_npm_latest_version:
try:
logger.info(f'Updating CDK to latest')
# uninstall and reinstall cdk in case npm has been updated.
output = process_utils.check_output(
'npm uninstall -g aws-cdk',
cwd=self._cdk_path,
shell=True)
logger.info(f'Uninstall CDK output: {output}')
output = process_utils.check_output(
'npm install -g aws-cdk@latest',
cwd=self._cdk_path,
shell=True)
logger.info(f'Install CDK output: {output}')
except subprocess.CalledProcessError as error:
logger.warning(f'Failed reinstalling latest CDK on npm'
f'\nError:{error.stderr}')
def setup(self, cdk_path: str, project: str, account_id: str,
workspace: pytest.fixture, session: boto3.session.Session, bootstrap_required: bool):
"""
:param cdk_path: Path where cdk app.py is stored.
:param project: Project name used for cdk project name env variable.
:param account_id: AWS account id to use with cdk application.
:param workspace: ly_test_tools workspace fixture.
:param workspace: bootstrap_required deploys bootstrap stack.
"""
self._cdk_env = os.environ.copy()
self._cdk_env['O3DE_AWS_PROJECT_NAME'] = project
unique_id = uuid.uuid4().hex[-4:]
self._cdk_env['O3DE_AWS_PROJECT_NAME'] = project[:4] + unique_id if len(project) > 4 else project + unique_id
self._cdk_env['O3DE_AWS_DEPLOY_REGION'] = session.region_name
self._cdk_env['O3DE_AWS_DEPLOY_ACCOUNT'] = account_id
self._cdk_env['PATH'] = f'{workspace.paths.engine_root()}\\python;' + self._cdk_env['PATH']
@@ -43,27 +96,37 @@ class Cdk:
self._cdk_env['AWS_ACCESS_KEY_ID'] = credentials.access_key
self._cdk_env['AWS_SECRET_ACCESS_KEY'] = credentials.secret_key
self._cdk_env['AWS_SESSION_TOKEN'] = credentials.token
self._stacks = []
self._cdk_path = cdk_path
self._session = session
output = process_utils.check_output(
'python -m pip install -r requirements.txt',
cwd=self._cdk_path,
env=self._cdk_env,
shell=True)
logger.info(f'Installing cdk python dependencies: {output}')
if bootstrap_required:
self.bootstrap()
def bootstrap(self) -> None:
"""
Deploy the bootstrap stack.
"""
bootstrap_cmd = ['cdk', 'bootstrap',
f'aws://{self._cdk_env["O3DE_AWS_DEPLOY_ACCOUNT"]}/{self._cdk_env["O3DE_AWS_DEPLOY_REGION"]}']
try:
bootstrap_cmd = ['cdk', 'bootstrap',
f'aws://{self._cdk_env["O3DE_AWS_DEPLOY_ACCOUNT"]}/{self._cdk_env["O3DE_AWS_DEPLOY_REGION"]}']
process_utils.check_call(
bootstrap_cmd,
cwd=self._cdk_path,
env=self._cdk_env,
shell=True)
process_utils.check_call(
bootstrap_cmd,
cwd=self._cdk_path,
env=self._cdk_env,
shell=True)
except botocore.exceptions.ClientError as clientError:
logger.warning(f'Failed creating Bootstrap stack {BOOTSTRAP_STACK_NAME} not found. '
f'\nError:{clientError["Error"]["Message"]}')
def list(self) -> List[str]:
"""
@@ -131,83 +194,51 @@ class Cdk:
"""
Destroys the cdk application.
"""
destroy_cdk_application_cmd = ['cdk', 'destroy', '-f']
process_utils.check_output(
destroy_cdk_application_cmd,
cwd=self._cdk_path,
env=self._cdk_env,
shell=True)
logger.info(f'CDK Path {self._cdk_path}')
destroy_cdk_application_cmd = ['cdk', 'destroy', '--all', '-f']
try:
process_utils.check_output(
destroy_cdk_application_cmd,
cwd=self._cdk_path,
env=self._cdk_env,
shell=True)
except subprocess.CalledProcessError as e:
logger.error(e.output)
raise e
self._stacks = []
self._cdk_path = ''
@staticmethod
def remove_bootstrap_stack(aws_utils: pytest.fixture) -> None:
def remove_bootstrap_stack(self) -> None:
"""
Remove the CDK bootstrap stack.
:param aws_utils: aws_utils fixture.
"""
# Check if the bootstrap stack exists.
response = aws_utils.client('cloudformation').describe_stacks(
response = self._session.client('cloudformation').describe_stacks(
StackName=BOOTSTRAP_STACK_NAME
)
stacks = response.get('Stacks', [])
if not stacks:
if not stacks or len(stacks) is 0:
return
# Clear the bootstrap staging bucket before deleting the bootstrap stack.
response = aws_utils.client('cloudformation').describe_stack_resource(
response = self._session.client('cloudformation').describe_stack_resource(
StackName=BOOTSTRAP_STACK_NAME,
LogicalResourceId=BOOTSTRAP_STAGING_BUCKET_LOGIC_ID
)
staging_bucket_name = response.get('StackResourceDetail', {}).get('PhysicalResourceId', '')
if staging_bucket_name:
s3 = aws_utils.resource('s3')
s3 = self._session.resource('s3')
bucket = s3.Bucket(staging_bucket_name)
for key in bucket.objects.all():
key.delete()
# Delete the bootstrap stack.
aws_utils.client('cloudformation').delete_stack(
StackName=BOOTSTRAP_STACK_NAME
)
@pytest.fixture(scope='function')
def cdk(
request: pytest.fixture,
project: str,
feature_name: str,
workspace: pytest.fixture,
aws_utils: pytest.fixture,
bootstrap_required: bool = True,
destroy_stacks_on_teardown: bool = True) -> Cdk:
"""
Fixture for setting up a Cdk
:param request: _pytest.fixtures.SubRequest class that handles getting
a pytest fixture from a pytest function/fixture.
:param project: Project name used for cdk project name env variable.
:param feature_name: Feature gem name to expect cdk folder in.
:param workspace: ly_test_tools workspace fixture.
:param aws_utils: aws_utils fixture.
:param bootstrap_required: Whether the bootstrap stack needs to be created to
provision resources the AWS CDK needs to perform the deployment.
:param destroy_stacks_on_teardown: option to control calling destroy ot the end of test.
:return Cdk class object.
"""
cdk_path = f'{workspace.paths.engine_root()}/Gems/{feature_name}/cdk'
cdk_obj = Cdk(cdk_path, project, aws_utils.assume_account_id(), workspace, aws_utils.assume_session())
if bootstrap_required:
cdk_obj.bootstrap()
def teardown():
if destroy_stacks_on_teardown:
cdk_obj.destroy()
cdk_obj.remove_bootstrap_stack(aws_utils)
request.addfinalizer(teardown)
return cdk_obj
# Should not need to delete the stack if S3 bucket can be cleaned.
# self._session.client('cloudformation').delete_stack(
# StackName=BOOTSTRAP_STACK_NAME
# )
@@ -12,9 +12,10 @@ import os
import logging
import ly_test_tools.log.log_monitor
# fixture imports
from AWS.Windows.resource_mappings.resource_mappings import resource_mappings
from AWS.Windows.cdk.cdk import cdk
from AWS.common.aws_utils import aws_utils
from AWS.Windows.cdk.cdk_utils import Cdk
from AWS.common.aws_utils import AwsUtils
from assetpipeline.ap_fixtures.asset_processor_fixture import asset_processor as asset_processor
AWS_PROJECT_NAME = 'AWS-AutomationTest'
@@ -75,5 +76,5 @@ class TestAWSClientAuthAnonymousCredentials(object):
expected_lines=['(Script) - Success anonymous credentials'],
unexpected_lines=['(Script) - Fail anonymous credentials'],
halt_on_unexpected=True,
)
)
assert result, 'Anonymous credentials fetched successfully.'
@@ -12,9 +12,10 @@ import os
import logging
import ly_test_tools.log.log_monitor
# fixture imports
from AWS.Windows.resource_mappings.resource_mappings import resource_mappings
from AWS.Windows.cdk.cdk import cdk
from AWS.common.aws_utils import aws_utils
from AWS.Windows.cdk.cdk_utils import Cdk
from AWS.common.aws_utils import AwsUtils
from assetpipeline.ap_fixtures.asset_processor_fixture import asset_processor as asset_processor
AWS_PROJECT_NAME = 'AWS-AutomationTest'
@@ -8,11 +8,12 @@ remove or modify any license notices. This file is distributed on an "AS IS" BAS
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
import boto3
import pytest
import logging
logger = logging.getLogger(__name__)
logging.getLogger('boto').setLevel(logging.CRITICAL)
logging.getLogger('boto3').setLevel(logging.WARNING)
logging.getLogger('botocore').setLevel(logging.WARNING)
logging.getLogger('nose').setLevel(logging.WARNING)
class AwsUtils:
@@ -63,28 +64,3 @@ class AwsUtils:
clears stored session
"""
self._assume_session = None
@pytest.fixture(scope='function')
def aws_utils(
request: pytest.fixture,
assume_role_arn: str,
session_name: str,
region_name: str):
"""
Fixture for AWS util functions
:param request: _pytest.fixtures.SubRequest class that handles getting
a pytest fixture from a pytest function/fixture.
:param assume_role_arn: Role used to fetch temporary aws credentials, configure service clients with obtained credentials.
:param session_name: Session name to set.
:param region_name: AWS account region to set for session.
:return AWSUtils class object.
"""
aws_utils_obj = AwsUtils(assume_role_arn, session_name, region_name)
def teardown():
aws_utils_obj.destroy()
request.addfinalizer(teardown)
return aws_utils_obj
@@ -0,0 +1,87 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
import pytest
import logging
from AWS.common.aws_utils import AwsUtils
from AWS.Windows.cdk.cdk_utils import Cdk
logger = logging.getLogger(__name__)
@pytest.fixture(scope='function')
def aws_utils(
request: pytest.fixture,
assume_role_arn: str,
session_name: str,
region_name: str):
"""
Fixture for AWS util functions
:param request: _pytest.fixtures.SubRequest class that handles getting
a pytest fixture from a pytest function/fixture.
:param assume_role_arn: Role used to fetch temporary aws credentials, configure service clients with obtained credentials.
:param session_name: Session name to set.
:param region_name: AWS account region to set for session.
:return AWSUtils class object.
"""
aws_utils_obj = AwsUtils(assume_role_arn, session_name, region_name)
def teardown():
aws_utils_obj.destroy()
request.addfinalizer(teardown)
return aws_utils_obj
# Set global pytest variable for cdk to avoid recreating instance
pytest.cdk_obj = None
@pytest.fixture(scope='function')
def cdk(
request: pytest.fixture,
project: str,
feature_name: str,
workspace: pytest.fixture,
aws_utils: pytest.fixture,
bootstrap_required: bool = True,
destroy_stacks_on_teardown: bool = True) -> Cdk:
"""
Fixture for setting up a Cdk
:param request: _pytest.fixtures.SubRequest class that handles getting
a pytest fixture from a pytest function/fixture.
:param project: Project name used for cdk project name env variable.
:param feature_name: Feature gem name to expect cdk folder in.
:param workspace: ly_test_tools workspace fixture.
:param aws_utils: aws_utils fixture.
:param bootstrap_required: Whether the bootstrap stack needs to be created to
provision resources the AWS CDK needs to perform the deployment.
:param destroy_stacks_on_teardown: option to control calling destroy ot the end of test.
:return Cdk class object.
"""
cdk_path = f'{workspace.paths.engine_root()}/Gems/{feature_name}/cdk'
logger.info(f'CDK Path {cdk_path}')
if pytest.cdk_obj is None:
pytest.cdk_obj = Cdk()
pytest.cdk_obj.setup(cdk_path, project, aws_utils.assume_account_id(), workspace, aws_utils.assume_session(),
bootstrap_required)
def teardown():
if destroy_stacks_on_teardown:
pytest.cdk_obj.destroy()
# Enable after https://github.com/aws/aws-cdk/issues/986 is fixed.
# Until then clean the bootstrap bucket manually.
# cdk_obj.remove_bootstrap_stack()
request.addfinalizer(teardown)
return pytest.cdk_obj
@@ -60,5 +60,4 @@ add_subdirectory(streaming)
add_subdirectory(smoke)
## AWS ##
# Enable when AWS Gems work on Linux and Android.
# add_subdirectory(AWS)
add_subdirectory(AWS)
@@ -24,12 +24,11 @@ from base import TestAutomationBase
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(TestAutomationBase):
@pytest.mark.xfail(reason="Running with atom null renderer is causing this test to fail")
def test_C18977329_NvCloth_AddClothSimulationToMesh(self, request, workspace, editor, launcher_platform):
from . import C18977329_NvCloth_AddClothSimulationToMesh as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.xfail(reason="Running with atom null renderer is causing this test to fail")
def test_C18977330_NvCloth_AddClothSimulationToActor(self, request, workspace, editor, launcher_platform):
from . import C18977330_NvCloth_AddClothSimulationToActor as test_module
self._run_test(request, workspace, editor, test_module)
@@ -102,7 +102,7 @@ def C4044695_PhysXCollider_AddMultipleSurfaceFbx():
# 6) Check if multiple material slots show up under Materials section in the PhysX Collider component
pte = collider_component.get_property_tree()
def get_surface_count():
count = pte.get_container_count("Collider Configuration|Physics Material|Mesh Surfaces")
count = pte.get_container_count("Collider Configuration|Physics Materials|Slots")
return count.GetValue()
Report.result(
@@ -93,11 +93,13 @@ class TestAutomation(TestAutomationBase):
self._run_test(request, workspace, editor, test_module)
@revert_physics_config
@fm.file_override('physxsystemconfiguration.setreg','C4044457_Material_RestitutionCombine.setreg_override', 'AutomatedTesting/Registry')
def test_C4044457_Material_RestitutionCombine(self, request, workspace, editor, launcher_platform):
from . import C4044457_Material_RestitutionCombine as test_module
self._run_test(request, workspace, editor, test_module)
@revert_physics_config
@fm.file_override('physxsystemconfiguration.setreg','C4044456_Material_FrictionCombine.setreg_override', 'AutomatedTesting/Registry')
def test_C4044456_Material_FrictionCombine(self, request, workspace, editor, launcher_platform):
from . import C4044456_Material_FrictionCombine as test_module
self._run_test(request, workspace, editor, test_module)
@@ -194,6 +196,7 @@ class TestAutomation(TestAutomationBase):
self._run_test(request, workspace, editor, test_module)
@revert_physics_config
@fm.file_override('physxsystemconfiguration.setreg','C18981526_Material_RestitutionCombinePriority.setreg_override', 'AutomatedTesting/Registry')
def test_C18981526_Material_RestitutionCombinePriority(self, request, workspace, editor, launcher_platform):
from . import C18981526_Material_RestitutionCombinePriority as test_module
self._run_test(request, workspace, editor, test_module)
@@ -229,6 +232,7 @@ class TestAutomation(TestAutomationBase):
self._run_test(request, workspace, editor, test_module)
@revert_physics_config
@fm.file_override('physxsystemconfiguration.setreg','C18977601_Material_FrictionCombinePriority.setreg_override', 'AutomatedTesting/Registry')
def test_C18977601_Material_FrictionCombinePriority(self, request, workspace, editor, launcher_platform):
from . import C18977601_Material_FrictionCombinePriority as test_module
self._run_test(request, workspace, editor, test_module)
@@ -250,6 +254,7 @@ class TestAutomation(TestAutomationBase):
@pytest.mark.xfail(
reason="This test needs new physics asset with multiple materials to be more stable.")
@revert_physics_config
@fm.file_override('physxsystemconfiguration.setreg','C4044697_Material_PerfaceMaterialValidation.setreg_override', 'AutomatedTesting/Registry')
def test_C4044697_Material_PerfaceMaterialValidation(self, request, workspace, editor, launcher_platform):
from . import C4044697_Material_PerfaceMaterialValidation as test_module
self._run_test(request, workspace, editor, test_module)
@@ -282,6 +287,7 @@ class TestAutomation(TestAutomationBase):
self._run_test(request, workspace, editor, test_module)
@revert_physics_config
@fm.file_override('physxsystemconfiguration.setreg','C15556261_PhysXMaterials_CharacterControllerMaterialAssignment.setreg_override', 'AutomatedTesting/Registry')
def test_C15556261_PhysXMaterials_CharacterControllerMaterialAssignment(self, request, workspace, editor, launcher_platform):
from . import C15556261_PhysXMaterials_CharacterControllerMaterialAssignment as test_module
self._run_test(request, workspace, editor, test_module)
@@ -326,6 +332,7 @@ class TestAutomation(TestAutomationBase):
self._run_test(request, workspace, editor, test_module)
@revert_physics_config
@fm.file_override('physxsystemconfiguration.setreg','C4044461_Material_Restitution.setreg_override', 'AutomatedTesting/Registry')
def test_C4044461_Material_Restitution(self, request, workspace, editor, launcher_platform):
from . import C4044461_Material_Restitution as test_module
self._run_test(request, workspace, editor, test_module)
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f0f4d4e0155feaa76c80a14128000a0fd9570ab76e79f4847eaef9006324a4d2
size 9084
File diff suppressed because it is too large Load Diff
@@ -0,0 +1 @@
<Environment />
@@ -0,0 +1 @@
<TimeOfDay Time="12"/>
@@ -0,0 +1,6 @@
<download name="ClientAuth" type="Map">
<index src="filelist.xml" dest="filelist.xml"/>
<files>
<file src="level.pak" dest="level.pak" size="ED0" md5="dbf5115226e4b0ea38ebdc3967ba3aa9"/>
</files>
</download>
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:da041115014f11696d5878d5c21247c17b8d694fa9674e30692259261a7223a2
size 3792
@@ -0,0 +1,12 @@
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:43b1a23b62fe2ffa05545ac99524f40b6fff49d6e35925b9d6138c00d8082e86
size 9073
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,6 @@
<download name="ClientAuthPasswordSignIn" type="Map">
<index src="filelist.xml" dest="filelist.xml"/>
<files>
<file src="level.pak" dest="level.pak" size="DDF" md5="ebe91ae5f1ea1ec735b6650b14f3f95b"/>
</files>
</download>
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a58292341785cb260dc0ccf346259e35e2817ee48fc401a21ab528f6afb97b52
size 3551
@@ -0,0 +1,12 @@
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f3d5121b26608b02747e245071ccff29ac57358cb6349ec9495a7a003ac12467
size 8942
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,6 @@
<download name="ClientAuthPasswordSignUp" type="Map">
<index src="filelist.xml" dest="filelist.xml"/>
<files>
<file src="level.pak" dest="level.pak" size="DDA" md5="aa6df891d505d6d9175beee4b55626db"/>
</files>
</download>
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:605391d415b828b100bada11d108099520c0b6a020f17588887b610475805d90
size 3546
@@ -0,0 +1,12 @@
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:023992998ab5a1d64b38dacd1d5e1a9dc930ff704289c0656ed6eaba6951d660
size 9066
@@ -0,0 +1,79 @@
----------------------------------------------------------------------------------------------------
--
-- All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
-- its licensors.
--
-- For complete copyright and license terms please see the LICENSE at the root of this
-- distribution (the "License"). All use of this software is governed by the License,
-- or, if provided, by the license below or the license accompanying this file. Do not
-- remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--
--
----------------------------------------------------------------------------------------------------
local metrics = {
}
function metrics:OnActivate()
self.tickTime = 0
self.numSubmittedMetricsEvents = 0
self.tickBusHandler = TickBus.Connect(self,self.entityId)
self.metricsNotificationHandler = AWSMetricsNotificationBus.Connect(self, self.entityId)
LyShineLua.ShowMouseCursor(true)
end
function metrics:OnSendMetricsSuccess(requestId)
Debug.Log("Metrics is sent successfully.")
end
function metrics:OnSendMetricsFailure(requestId, errorMessage)
Debug.Log("Failed to send metrics.")
end
function metrics:OnDeactivate()
AWSMetricsRequestBus.Broadcast.FlushMetrics()
Debug.Log("Stop generating new test events and flushed the buffered metrics.")
self.tickBusHandler:Disconnect()
self.metricsNotificationHandler:Disconnect()
end
function metrics:OnTick(deltaTime, timePoint)
self.tickTime = self.tickTime + deltaTime
if self.tickTime > 2.0 then
defaultAttribute = AWSMetrics_MetricsAttribute()
defaultAttribute:SetName("event_name")
defaultAttribute:SetStrValue("login")
customAttribute = AWSMetrics_MetricsAttribute()
customAttribute:SetName("custom_attribute")
customAttribute:SetStrValue("value")
attributeList = AWSMetrics_AttributesSubmissionList()
attributeList.attributes:push_back(defaultAttribute)
attributeList.attributes:push_back(customAttribute)
if self.numSubmittedMetricsEvents % 2 == 0 then
if AWSMetricsRequestBus.Broadcast.SubmitMetrics(attributeList.attributes, 0, "lua", false) then
Debug.Log("Submitted metrics without buffer.")
else
Debug.Log("Failed to Submit metrics without buffer.")
end
else
if AWSMetricsRequestBus.Broadcast.SubmitMetrics(attributeList.attributes, 0, "lua", true) then
Debug.Log("Submitted metrics with buffer.")
else
Debug.Log("Failed to Submit metrics with buffer.")
end
end
self.numSubmittedMetricsEvents = self.numSubmittedMetricsEvents + 1
self.tickTime = 0
end
end
return metrics
@@ -0,0 +1,6 @@
<download name="Metrics" type="Map">
<index src="filelist.xml" dest="filelist.xml"/>
<files>
<file src="level.pak" dest="level.pak" size="E09" md5="f16fff2970a4037af5909b269ceece4b"/>
</files>
</download>
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e7c0c07b13bb64db344b94d5712e1e802e607a9dee506768b34481f4a76d8505
size 3593
@@ -0,0 +1,12 @@
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
0,0,0,0,0,0
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e15d484113e8151072b410924747a8ad304f6f12457fad577308c0491693ab34
size 5472
oid sha256:6517300fb1ce70c4696286e14715c547cfd175eabbb2042f7f2a456b15054224
size 5253
@@ -1,6 +1,6 @@
<download name="C18977329_NvCloth_AddClothSimulationToMesh" type="Map">
<index src="filelist.xml" dest="filelist.xml"/>
<files>
<file src="level.pak" dest="level.pak" size="97C8" md5="64e64e1e3345dacace01dde152c72250"/>
<file src="level.pak" dest="level.pak" size="96A3" md5="db8a235878128076005ade018587f536"/>
</files>
</download>
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:64de37c805b0be77cdb7a85b5406af58b7f845e7d97fec1721ac5d789bb641db
size 38856
oid sha256:ce32a7cdf3ed37751385b3bb18f05206702978363f325d06727b5eb20d40b7eb
size 38563
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7b595323d4d51211463dea0338abb6ce2a4a0a8d41efb12ac3c9dccd1f972171
size 5504
oid sha256:89dbcec013cb819e52ec0f8fed0a9e417fd32eac8aeb67d3958266bb6089ec21
size 5505
@@ -1,6 +1,6 @@
<download name="C18977330_NvCloth_AddClothSimulationToActor" type="Map">
<index src="filelist.xml" dest="filelist.xml"/>
<files>
<file src="level.pak" dest="level.pak" size="9941" md5="297730934d657d7ca57a7357ee9cd566"/>
<file src="level.pak" dest="level.pak" size="E16" md5="036b4a87cbb256f76823549ab4b18c05"/>
</files>
</download>
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:617c455668fc41cb7fd69de690e4aa3c80f2cb36deaa371902b79de18fcd1cb2
size 39233
oid sha256:622c2624b04e07b704520f32c458b50d5a50de1ef116b7bc9c3c0ccb6f4a4ecc
size 3606
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b6e408095c15a388768b7f70b6049f33c894aab3e51f2d744bc1ae1d18668ee4
size 9694
oid sha256:824a51a375f19274d5698ff08af0fdc3dc18204505c73a943de748455d108b01
size 6181
@@ -0,0 +1,115 @@
{
"Amazon": {
"Gems": {
"PhysX": {
"PhysXSystemConfiguration": {
"CollisionConfig": {
"Layers": {
"LayerNames": [
"Default",
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
"TouchBend"
]
},
"Groups": {
"GroupPresets": [
{
"Name": "All",
"ReadOnly": true
},
{
"Id": {
"GroupId": "{CDB6B8D8-5CD0-40A8-874D-839B00A92EBB}"
},
"Name": "None",
"Group": {
"Mask": 0
},
"ReadOnly": true
},
{
"Id": {
"GroupId": "{22769429-5D46-429B-829A-0115239D9AAA}"
},
"Name": "All_NoTouchBend",
"Group": {
"Mask": 9223372036854775807
},
"ReadOnly": true
}
]
}
},
"MaterialLibrary": {
"assetId": {
"guid": "{70D4A444-AFD4-57C4-9885-63F25AC3C281}"
},
"loadBehavior": "QueueLoad",
"assetHint": "levels/physics/c15556261_physxmaterials_charactercontrollermaterialassignment/library.physmaterial"
}
}
}
}
}
}
@@ -0,0 +1,115 @@
{
"Amazon": {
"Gems": {
"PhysX": {
"PhysXSystemConfiguration": {
"CollisionConfig": {
"Layers": {
"LayerNames": [
"Default",
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
"TouchBend"
]
},
"Groups": {
"GroupPresets": [
{
"Name": "All",
"ReadOnly": true
},
{
"Id": {
"GroupId": "{CDB6B8D8-5CD0-40A8-874D-839B00A92EBB}"
},
"Name": "None",
"Group": {
"Mask": 0
},
"ReadOnly": true
},
{
"Id": {
"GroupId": "{22769429-5D46-429B-829A-0115239D9AAA}"
},
"Name": "All_NoTouchBend",
"Group": {
"Mask": 9223372036854775807
},
"ReadOnly": true
}
]
}
},
"MaterialLibrary": {
"assetId": {
"guid": "{B8749DAB-15DA-5A61-B565-C853673604CD}"
},
"loadBehavior": "QueueLoad",
"assetHint": "levels/physics/c18977601_material_frictioncombinepriority/friction_combine.physmaterial"
}
}
}
}
}
}
@@ -0,0 +1,115 @@
{
"Amazon": {
"Gems": {
"PhysX": {
"PhysXSystemConfiguration": {
"CollisionConfig": {
"Layers": {
"LayerNames": [
"Default",
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
"TouchBend"
]
},
"Groups": {
"GroupPresets": [
{
"Name": "All",
"ReadOnly": true
},
{
"Id": {
"GroupId": "{CDB6B8D8-5CD0-40A8-874D-839B00A92EBB}"
},
"Name": "None",
"Group": {
"Mask": 0
},
"ReadOnly": true
},
{
"Id": {
"GroupId": "{22769429-5D46-429B-829A-0115239D9AAA}"
},
"Name": "All_NoTouchBend",
"Group": {
"Mask": 9223372036854775807
},
"ReadOnly": true
}
]
}
},
"MaterialLibrary": {
"assetId": {
"guid": "{AED48B18-0F3F-5E59-A8FF-30DB134B307B}"
},
"loadBehavior": "QueueLoad",
"assetHint": "levels/physics/c18981526_material_restitutioncombinepriority/restitution_combine.physmaterial"
}
}
}
}
}
}
@@ -0,0 +1,115 @@
{
"Amazon": {
"Gems": {
"PhysX": {
"PhysXSystemConfiguration": {
"CollisionConfig": {
"Layers": {
"LayerNames": [
"Default",
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
"TouchBend"
]
},
"Groups": {
"GroupPresets": [
{
"Name": "All",
"ReadOnly": true
},
{
"Id": {
"GroupId": "{CDB6B8D8-5CD0-40A8-874D-839B00A92EBB}"
},
"Name": "None",
"Group": {
"Mask": 0
},
"ReadOnly": true
},
{
"Id": {
"GroupId": "{22769429-5D46-429B-829A-0115239D9AAA}"
},
"Name": "All_NoTouchBend",
"Group": {
"Mask": 9223372036854775807
},
"ReadOnly": true
}
]
}
},
"MaterialLibrary": {
"assetId": {
"guid": "{8D2C4A29-E0FC-564F-82C9-24BBA30C5A90}"
},
"loadBehavior": "QueueLoad",
"assetHint": "levels/physics/c4044456_material_frictioncombine/friction_combine.physmaterial"
}
}
}
}
}
}
@@ -0,0 +1,115 @@
{
"Amazon": {
"Gems": {
"PhysX": {
"PhysXSystemConfiguration": {
"CollisionConfig": {
"Layers": {
"LayerNames": [
"Default",
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
"TouchBend"
]
},
"Groups": {
"GroupPresets": [
{
"Name": "All",
"ReadOnly": true
},
{
"Id": {
"GroupId": "{CDB6B8D8-5CD0-40A8-874D-839B00A92EBB}"
},
"Name": "None",
"Group": {
"Mask": 0
},
"ReadOnly": true
},
{
"Id": {
"GroupId": "{22769429-5D46-429B-829A-0115239D9AAA}"
},
"Name": "All_NoTouchBend",
"Group": {
"Mask": 9223372036854775807
},
"ReadOnly": true
}
]
}
},
"MaterialLibrary": {
"assetId": {
"guid": "{D5D6A6DE-E636-5638-B30D-6CE2FDC321F8}"
},
"loadBehavior": "QueueLoad",
"assetHint": "levels/physics/c4044457_material_restitutioncombine/restitution_combine.physmaterial"
}
}
}
}
}
}
@@ -0,0 +1,115 @@
{
"Amazon": {
"Gems": {
"PhysX": {
"PhysXSystemConfiguration": {
"CollisionConfig": {
"Layers": {
"LayerNames": [
"Default",
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
"TouchBend"
]
},
"Groups": {
"GroupPresets": [
{
"Name": "All",
"ReadOnly": true
},
{
"Id": {
"GroupId": "{CDB6B8D8-5CD0-40A8-874D-839B00A92EBB}"
},
"Name": "None",
"Group": {
"Mask": 0
},
"ReadOnly": true
},
{
"Id": {
"GroupId": "{22769429-5D46-429B-829A-0115239D9AAA}"
},
"Name": "All_NoTouchBend",
"Group": {
"Mask": 9223372036854775807
},
"ReadOnly": true
}
]
}
},
"MaterialLibrary": {
"assetId": {
"guid": "{E4117B5B-8D9A-5C1D-BA1E-C36542A6588D}"
},
"loadBehavior": "QueueLoad",
"assetHint": "levels/physics/c4044461_material_restitution/restitution.physmaterial"
}
}
}
}
}
}
@@ -0,0 +1,115 @@
{
"Amazon": {
"Gems": {
"PhysX": {
"PhysXSystemConfiguration": {
"CollisionConfig": {
"Layers": {
"LayerNames": [
"Default",
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
"TouchBend"
]
},
"Groups": {
"GroupPresets": [
{
"Name": "All",
"ReadOnly": true
},
{
"Id": {
"GroupId": "{CDB6B8D8-5CD0-40A8-874D-839B00A92EBB}"
},
"Name": "None",
"Group": {
"Mask": 0
},
"ReadOnly": true
},
{
"Id": {
"GroupId": "{22769429-5D46-429B-829A-0115239D9AAA}"
},
"Name": "All_NoTouchBend",
"Group": {
"Mask": 9223372036854775807
},
"ReadOnly": true
}
]
}
},
"MaterialLibrary": {
"assetId": {
"guid": "{2E85B457-ED19-5FE3-90B4-6EFFB4D0E682}"
},
"loadBehavior": "QueueLoad",
"assetHint": "levels/physics/c4044697_material_perfacematerialvalidation/test_library.physmaterial"
}
}
}
}
}
}
File diff suppressed because it is too large Load Diff
@@ -36,6 +36,12 @@ namespace AZ
Color* color = reinterpret_cast<Color*>(outputValue);
AZ_Assert(color, "Output value for JsonColorSerializer can't be null.");
if (IsExplicitDefault(inputValue))
{
*color = Color::CreateZero();
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed, "Color value set to default of zero.");
}
switch (inputValue.GetType())
{
case rapidjson::kArrayType:
@@ -43,10 +49,14 @@ namespace AZ
case rapidjson::kObjectType:
return LoadObject(*color, inputValue, context);
case rapidjson::kStringType: // fall through
case rapidjson::kNumberType: // fall through
case rapidjson::kNullType: // fall through
case rapidjson::kFalseType: // fall through
case rapidjson::kStringType:
[[fallthrough]];
case rapidjson::kNumberType:
[[fallthrough]];
case rapidjson::kNullType:
[[fallthrough]];
case rapidjson::kFalseType:
[[fallthrough]];
case rapidjson::kTrueType:
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Unsupported type. Colors can only be read from arrays or objects.");
@@ -91,6 +101,11 @@ namespace AZ
}
}
auto JsonColorSerializer::GetOperationsFlags() const -> OperationFlags
{
return OperationFlags::InitializeNewInstance;
}
JsonSerializationResult::Result JsonColorSerializer::LoadObject(Color& output, const rapidjson::Value& inputValue,
JsonDeserializerContext& context)
{
@@ -28,6 +28,8 @@ namespace AZ
JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
const Uuid& valueTypeId, JsonSerializerContext& context) override;
OperationFlags GetOperationsFlags() const override;
private:
enum class LoadAlpha
{
@@ -263,7 +263,7 @@ namespace AZ::JsonMathMatrixSerializerInternal
template<typename MatrixType, size_t RowCount, size_t ColumnCount>
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
const rapidjson::Value& inputValue, JsonDeserializerContext& context, bool isExplicitDefault)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
@@ -279,6 +279,12 @@ namespace AZ::JsonMathMatrixSerializerInternal
MatrixType* matrix = reinterpret_cast<MatrixType*>(outputValue);
AZ_Assert(matrix, "Output value for JsonMatrix%zux%zuSerializer can't be null.", RowCount, ColumnCount);
if (isExplicitDefault)
{
*matrix = MatrixType::CreateIdentity();
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed, "Matrix value set to identity matrix.");
}
switch (inputValue.GetType())
{
case rapidjson::kArrayType:
@@ -381,6 +387,16 @@ namespace AZ::JsonMathMatrixSerializerInternal
namespace AZ
{
// BaseJsonMatrixSerializer
AZ_CLASS_ALLOCATOR_IMPL(BaseJsonMatrixSerializer, SystemAllocator, 0);
auto BaseJsonMatrixSerializer::GetOperationsFlags() const -> OperationFlags
{
return OperationFlags::InitializeNewInstance;
}
// Matrix3x3
AZ_CLASS_ALLOCATOR_IMPL(JsonMatrix3x3Serializer, SystemAllocator, 0);
@@ -389,10 +405,7 @@ namespace AZ
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
return JsonMathMatrixSerializerInternal::Load<Matrix3x3, 3, 3>(
outputValue,
outputValueTypeId,
inputValue,
context);
outputValue, outputValueTypeId, inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonMatrix3x3Serializer::Store(rapidjson::Value& outputValue, const void* inputValue,
@@ -401,11 +414,7 @@ namespace AZ
outputValue.SetObject();
return JsonMathMatrixSerializerInternal::StoreRotationAndScale<Matrix3x3>(
outputValue,
inputValue,
defaultValue,
valueTypeId,
context);
outputValue, inputValue, defaultValue, valueTypeId, context);
}
@@ -417,10 +426,7 @@ namespace AZ
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
return JsonMathMatrixSerializerInternal::Load<Matrix3x4, 3, 4>(
outputValue,
outputValueTypeId,
inputValue,
context);
outputValue, outputValueTypeId, inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonMatrix3x4Serializer::Store(rapidjson::Value& outputValue, const void* inputValue,
@@ -428,19 +434,11 @@ namespace AZ
{
outputValue.SetObject();
auto result = JsonMathMatrixSerializerInternal::StoreRotationAndScale<Matrix3x4>(
outputValue,
inputValue,
defaultValue,
valueTypeId,
context);
auto result =
JsonMathMatrixSerializerInternal::StoreRotationAndScale<Matrix3x4>(outputValue, inputValue, defaultValue, valueTypeId, context);
auto resultTranslation = JsonMathMatrixSerializerInternal::StoreTranslation<Matrix3x4>(
outputValue,
inputValue,
defaultValue,
valueTypeId,
context);
auto resultTranslation =
JsonMathMatrixSerializerInternal::StoreTranslation<Matrix3x4>(outputValue, inputValue, defaultValue, valueTypeId, context);
result.GetResultCode().Combine(resultTranslation);
return result;
@@ -454,10 +452,7 @@ namespace AZ
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
return JsonMathMatrixSerializerInternal::Load<Matrix4x4, 4, 4>(
outputValue,
outputValueTypeId,
inputValue,
context);
outputValue, outputValueTypeId, inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonMatrix4x4Serializer::Store(rapidjson::Value& outputValue, const void* inputValue,
@@ -465,19 +460,11 @@ namespace AZ
{
outputValue.SetObject();
auto result = JsonMathMatrixSerializerInternal::StoreRotationAndScale<Matrix4x4>(
outputValue,
inputValue,
defaultValue,
valueTypeId,
context);
auto result =
JsonMathMatrixSerializerInternal::StoreRotationAndScale<Matrix4x4>(outputValue, inputValue, defaultValue, valueTypeId, context);
auto resultTranslation = JsonMathMatrixSerializerInternal::StoreTranslation<Matrix4x4>(
outputValue,
inputValue,
defaultValue,
valueTypeId,
context);
auto resultTranslation =
JsonMathMatrixSerializerInternal::StoreTranslation<Matrix4x4>(outputValue, inputValue, defaultValue, valueTypeId, context);
result.GetResultCode().Combine(resultTranslation);
return result;
@@ -16,11 +16,18 @@
namespace AZ
{
class JsonMatrix3x3Serializer
: public BaseJsonSerializer
class BaseJsonMatrixSerializer : public BaseJsonSerializer
{
public:
AZ_RTTI(JsonMatrix3x3Serializer, "{8C76CD6A-8576-4604-A746-CF7A7F20F366}", BaseJsonSerializer);
AZ_RTTI(BaseJsonMatrixSerializer, "{18CA4637-C9B7-454B-9126-107E18A8C096}", BaseJsonSerializer);
AZ_CLASS_ALLOCATOR_DECL;
OperationFlags GetOperationsFlags() const override;
};
class JsonMatrix3x3Serializer : public BaseJsonMatrixSerializer
{
public:
AZ_RTTI(JsonMatrix3x3Serializer, "{8C76CD6A-8576-4604-A746-CF7A7F20F366}", BaseJsonMatrixSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -28,11 +35,10 @@ namespace AZ
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
class JsonMatrix3x4Serializer
: public BaseJsonSerializer
class JsonMatrix3x4Serializer : public BaseJsonMatrixSerializer
{
public:
AZ_RTTI(JsonMatrix3x4Serializer, "{E801333B-4AF1-4F43-976C-579670B02DC5}", BaseJsonSerializer);
AZ_RTTI(JsonMatrix3x4Serializer, "{E801333B-4AF1-4F43-976C-579670B02DC5}", BaseJsonMatrixSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -40,11 +46,10 @@ namespace AZ
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
class JsonMatrix4x4Serializer
: public BaseJsonSerializer
class JsonMatrix4x4Serializer : public BaseJsonMatrixSerializer
{
public:
AZ_RTTI(JsonMatrix4x4Serializer, "{46E888FC-248A-4910-9221-4E101A10AEA1}", BaseJsonSerializer);
AZ_RTTI(JsonMatrix4x4Serializer, "{46E888FC-248A-4910-9221-4E101A10AEA1}", BaseJsonMatrixSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -124,7 +124,7 @@ namespace AZ
template<typename VectorType, size_t ElementCount>
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context)
JsonDeserializerContext& context, bool isExplicitDefault)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
@@ -138,6 +138,12 @@ namespace AZ
VectorType* vector = reinterpret_cast<VectorType*>(outputValue);
AZ_Assert(vector, "Output value for JsonVector%iSerializer can't be null.", ElementCount);
if (isExplicitDefault)
{
*vector = VectorType::CreateZero();
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed, "Math vector value set to default of zero.");
}
switch (inputValue.GetType())
{
case rapidjson::kArrayType:
@@ -145,10 +151,14 @@ namespace AZ
case rapidjson::kObjectType:
return LoadObject<VectorType, ElementCount>(*vector, inputValue, context);
case rapidjson::kStringType: // fall through
case rapidjson::kNumberType: // fall through
case rapidjson::kNullType: // fall through
case rapidjson::kFalseType: // fall through
case rapidjson::kStringType:
[[fallthrough]];
case rapidjson::kNumberType:
[[fallthrough]];
case rapidjson::kNullType:
[[fallthrough]];
case rapidjson::kFalseType:
[[fallthrough]];
case rapidjson::kTrueType:
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Unsupported type. Math vectors can only be read from arrays or objects.");
@@ -189,6 +199,16 @@ namespace AZ
}
}
// BaseJsonVectorSerializer
AZ_CLASS_ALLOCATOR_IMPL(BaseJsonVectorSerializer, SystemAllocator, 0);
auto BaseJsonVectorSerializer::GetOperationsFlags() const -> OperationFlags
{
return OperationFlags::InitializeNewInstance;
}
// Vector2
@@ -197,7 +217,8 @@ namespace AZ
JsonSerializationResult::Result JsonVector2Serializer::Load(void* outputValue, const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
return JsonMathVectorSerializerInternal::Load<Vector2, 2>(outputValue, outputValueTypeId, inputValue, context);
return JsonMathVectorSerializerInternal::Load<Vector2, 2>(
outputValue, outputValueTypeId, inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonVector2Serializer::Store(rapidjson::Value& outputValue, const void* inputValue,
@@ -214,7 +235,8 @@ namespace AZ
JsonSerializationResult::Result JsonVector3Serializer::Load(void* outputValue, const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
return JsonMathVectorSerializerInternal::Load<Vector3, 3>(outputValue, outputValueTypeId, inputValue, context);
return JsonMathVectorSerializerInternal::Load<Vector3, 3>(
outputValue, outputValueTypeId, inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonVector3Serializer::Store(rapidjson::Value& outputValue, const void* inputValue,
@@ -231,7 +253,8 @@ namespace AZ
JsonSerializationResult::Result JsonVector4Serializer::Load(void* outputValue, const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
return JsonMathVectorSerializerInternal::Load<Vector4, 4>(outputValue, outputValueTypeId, inputValue, context);
return JsonMathVectorSerializerInternal::Load<Vector4, 4>(
outputValue, outputValueTypeId, inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonVector4Serializer::Store(rapidjson::Value& outputValue, const void* inputValue,
@@ -252,7 +275,7 @@ namespace AZ
// check for "yaw, pitch, roll" object
if (inputValue.IsObject())
{
if (inputValue.GetObject().ObjectEmpty())
if (IsExplicitDefault(inputValue))
{
Quaternion* outQuaternion = reinterpret_cast<Quaternion*>(outputValue);
*outQuaternion = Quaternion::CreateIdentity();
@@ -283,7 +306,7 @@ namespace AZ
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Success, "Successfully read quaternion.");
}
return JsonMathVectorSerializerInternal::Load<Quaternion, 4>(outputValue, outputValueTypeId, inputValue, context);
return JsonMathVectorSerializerInternal::Load<Quaternion, 4>(outputValue, outputValueTypeId, inputValue, context, false);
}
JsonSerializationResult::Result JsonQuaternionSerializer::Store(rapidjson::Value& outputValue, const void* inputValue,
@@ -16,11 +16,18 @@
namespace AZ
{
class JsonVector2Serializer
: public BaseJsonSerializer
class BaseJsonVectorSerializer : public BaseJsonSerializer
{
public:
AZ_RTTI(JsonVector2Serializer, "{E1EAA209-9682-4120-B26B-3EDD9AD56D6F}", BaseJsonSerializer);
AZ_RTTI(BaseJsonVectorSerializer, "{C188D355-E6DF-4590-8B31-F40591F48A8E}", BaseJsonSerializer);
AZ_CLASS_ALLOCATOR_DECL;
OperationFlags GetOperationsFlags() const override;
};
class JsonVector2Serializer : public BaseJsonVectorSerializer
{
public:
AZ_RTTI(JsonVector2Serializer, "{E1EAA209-9682-4120-B26B-3EDD9AD56D6F}", BaseJsonVectorSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -28,11 +35,10 @@ namespace AZ
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
class JsonVector3Serializer
: public BaseJsonSerializer
class JsonVector3Serializer : public BaseJsonVectorSerializer
{
public:
AZ_RTTI(JsonVector3Serializer, "{BF82BBF3-3CD9-48DA-97CC-E4DF2EF01552}", BaseJsonSerializer);
AZ_RTTI(JsonVector3Serializer, "{BF82BBF3-3CD9-48DA-97CC-E4DF2EF01552}", BaseJsonVectorSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -40,11 +46,10 @@ namespace AZ
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
class JsonVector4Serializer
: public BaseJsonSerializer
class JsonVector4Serializer : public BaseJsonVectorSerializer
{
public:
AZ_RTTI(JsonVector4Serializer, "{05B45EA7-7102-4281-8AA0-2AC72D74AAFD}", BaseJsonSerializer);
AZ_RTTI(JsonVector4Serializer, "{05B45EA7-7102-4281-8AA0-2AC72D74AAFD}", BaseJsonVectorSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -52,11 +57,10 @@ namespace AZ
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
class JsonQuaternionSerializer
: public BaseJsonSerializer
class JsonQuaternionSerializer : public BaseJsonVectorSerializer
{
public:
AZ_RTTI(JsonQuaternionSerializer, "{18604375-3606-49AC-B366-0F6DF9149FF3}", BaseJsonSerializer);
AZ_RTTI(JsonQuaternionSerializer, "{18604375-3606-49AC-B366-0F6DF9149FF3}", BaseJsonVectorSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -33,6 +33,12 @@ namespace AZ
AZ::Transform* transformInstance = reinterpret_cast<AZ::Transform*>(outputValue);
AZ_Assert(transformInstance, "Output value for JsonTransformSerializer can't be null.");
if (IsExplicitDefault(inputValue))
{
*transformInstance = AZ::Transform::CreateIdentity();
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed, "Transform value set to identity.");
}
JSR::ResultCode result(JSR::Tasks::ReadField);
{
@@ -72,7 +78,7 @@ namespace AZ
return context.Report(
result,
result.GetProcessing() != JSR::Processing::Halted ? "Succesfully loaded Transform information."
result.GetProcessing() != JSR::Processing::Halted ? "Successfully loaded Transform information."
: "Failed to load Transform information.");
}
@@ -140,4 +146,9 @@ namespace AZ
: "Failed to store Transform information.");
}
auto JsonTransformSerializer::GetOperationsFlags() const -> OperationFlags
{
return OperationFlags::InitializeNewInstance;
}
} // namespace AZ
@@ -30,6 +30,8 @@ namespace AZ
rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue, const Uuid& valueTypeId,
JsonSerializerContext& context) override;
OperationFlags GetOperationsFlags() const override;
private:
// Note: These need to be defined as "const char[]" instead of "const char*" so that they can be implicitly converted
// to a rapidjson::GenericStringRef<>. (This also lets rapidjson get the string length at compile time)
@@ -33,6 +33,11 @@ namespace AZ
AZStd::regex_constants::icase | AZStd::regex_constants::optimize);
}
auto JsonUuidSerializer::GetOperationsFlags() const -> OperationFlags
{
return OperationFlags::InitializeNewInstance;
}
JsonSerializationResult::Result JsonUuidSerializer::Load(void* outputValue, const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
@@ -53,13 +58,24 @@ namespace AZ
Uuid* valAsUuid = reinterpret_cast<Uuid*>(outputValue);
if (IsExplicitDefault(inputValue))
{
*valAsUuid = AZ::Uuid::CreateNull();
return MessageResult("Uuid value set to default of null.", JSR::ResultCode(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed));
}
switch (inputValue.GetType())
{
case rapidjson::kArrayType: // fallthrough
case rapidjson::kObjectType:// fallthrough
case rapidjson::kFalseType: // fallthrough
case rapidjson::kTrueType: // fallthrough
case rapidjson::kNumberType:// fallthrough
case rapidjson::kArrayType:
[[fallthrough]];
case rapidjson::kObjectType:
[[fallthrough]];
case rapidjson::kFalseType:
[[fallthrough]];
case rapidjson::kTrueType:
[[fallthrough]];
case rapidjson::kNumberType:
[[fallthrough]];
case rapidjson::kNullType:
return MessageResult("Unsupported type. Uuids can only be read from strings.",
JSR::ResultCode(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported));
@@ -41,6 +41,8 @@ namespace AZ
JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
const Uuid& valueTypeId, JsonSerializerContext& context) override;
OperationFlags GetOperationsFlags() const override;
//! Does the same as load, but doesn't report through the provided callback in the settings. Instead the final
//! ResultCode and message are returned and it's up to the caller to report if need needed.
MessageResult UnreportedLoad(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue);
@@ -32,13 +32,24 @@ namespace AZ
switch (inputValue.GetType())
{
case rapidjson::kArrayType:
return LoadContainer(outputValue, outputValueTypeId, inputValue, context);
return LoadContainer(outputValue, outputValueTypeId, inputValue, false, context);
case rapidjson::kObjectType: // fall through
case rapidjson::kNullType: // fall through
case rapidjson::kStringType: // fall through
case rapidjson::kFalseType: // fall through
case rapidjson::kTrueType: // fall through
case rapidjson::kObjectType:
if (IsExplicitDefault(inputValue))
{
// Because this serializer has only the operation flag "InitializeNewInstance" set, the only time this will be called with
// an explicit default is when a new instance has been created.
return LoadContainer(outputValue, outputValueTypeId, inputValue, true, context);
}
[[fallthrough]];
case rapidjson::kNullType:
[[fallthrough]];
case rapidjson::kStringType:
[[fallthrough]];
case rapidjson::kFalseType:
[[fallthrough]];
case rapidjson::kTrueType:
[[fallthrough]];
case rapidjson::kNumberType:
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Unsupported type. AZStd::array entries can only be read from an array.");
@@ -129,7 +140,16 @@ namespace AZ
}
}
JsonSerializationResult::Result JsonArraySerializer::LoadContainer(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
auto JsonArraySerializer::GetOperationsFlags() const -> OperationFlags
{
return OperationFlags::InitializeNewInstance;
}
JsonSerializationResult::Result JsonArraySerializer::LoadContainer(
void* outputValue,
const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue,
bool isNewInstance,
JsonDeserializerContext& context)
{
namespace JSR = JsonSerializationResult; // Used to remove name conflicts in AzCore in uber builds.
@@ -154,14 +174,7 @@ namespace AZ
"Unable to retrieve the correct container information for AZStd::array instance.");
}
const size_t size = container->Size(outputValue);
if (inputValue.Size() < size)
{
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Not enough entries in JSON array to load an AZStd::array from.");
}
ContinuationFlags flags = ContinuationFlags::None;
ContinuationFlags flags = isNewInstance ? ContinuationFlags::LoadAsNewInstance : ContinuationFlags::None;
Uuid elementTypeId = Uuid::CreateNull();
auto typeEnumCallback = [&elementTypeId, &flags](const Uuid&, const SerializeContext::ClassElement* genericClassElement)
{
@@ -175,13 +188,23 @@ namespace AZ
};
container->EnumTypes(typeEnumCallback);
const size_t size = container->Size(outputValue);
if (!isNewInstance && inputValue.Size() < size)
{
return context.Report(
JSR::Tasks::ReadField, JSR::Outcomes::Unsupported, "Not enough entries in JSON array to load an AZStd::array from.");
}
rapidjson::Value explicitDefaultValue = GetExplicitDefault();
JSR::ResultCode retVal(JSR::Tasks::ReadField);
for (size_t i = 0; i < size; ++i)
{
ScopedContextPath subPath(context, i);
void* element = container->GetElementByIndex(outputValue, nullptr, i);
JSR::ResultCode result = ContinueLoading(element, elementTypeId, inputValue[aznumeric_caster(i)], context, flags);
JSR::ResultCode result = ContinueLoading(
element, elementTypeId, isNewInstance ? explicitDefaultValue : inputValue[aznumeric_caster(i)], context, flags);
if (result.GetProcessing() == JSR::Processing::Halted)
{
return context.Report(result, "Failed to load data to element in AZStd::array.");
@@ -189,15 +212,19 @@ namespace AZ
retVal.Combine(result);
}
if (container->Size(outputValue) == inputValue.Size())
if (isNewInstance)
{
return context.Report(retVal, "Filled new instance of AZStd::array with defaults.");
}
else if (container->Size(outputValue) == inputValue.Size())
{
return context.Report(retVal, "Successfully read entries into AZStd::array.");
}
else
{
retVal.Combine(JSR::ResultCode(JSR::Tasks::ReadField, JSR::Outcomes::Skipped));
return context.Report(retVal,
"Successfully read available entries into AZStd::array, but there were still values left in the JSON array.");
return context.Report(
retVal, "Successfully read available entries into AZStd::array, but there were still values left in the JSON array.");
}
}
} // namespace AZ
@@ -30,8 +30,14 @@ namespace AZ
JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
const Uuid& valueTypeId, JsonSerializerContext& context) override;
OperationFlags GetOperationsFlags() const override;
protected:
JsonSerializationResult::Result LoadContainer(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonSerializationResult::Result LoadContainer(
void* outputValue,
const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue,
bool isNewInstance,
JsonDeserializerContext& context);
};
} // namespace AZ
@@ -216,9 +216,10 @@ namespace AZ
JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueLoading(
void* object, const Uuid& typeId, const rapidjson::Value& value, JsonDeserializerContext& context, ContinuationFlags flags)
{
bool loadAsNewInstance = (flags & ContinuationFlags::LoadAsNewInstance) == ContinuationFlags::LoadAsNewInstance;
return (flags & ContinuationFlags::ResolvePointer) == ContinuationFlags::ResolvePointer
? JsonDeserializer::LoadToPointer(object, typeId, value, context)
: JsonDeserializer::Load(object, typeId, value, context);
: JsonDeserializer::Load(object, typeId, value, loadAsNewInstance, context);
}
JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueStoring(
@@ -163,15 +163,20 @@ namespace AZ
enum class ContinuationFlags
{
None = 0, //! No extra flags.
ResolvePointer = 1 << 0, //! The pointer passed in contains a pointer. The (de)serializer will attempt to resolve to an instance.
ReplaceDefault = 1 << 1 //! The default value provided for storing will be replaced with a newly created one.
None = 0, //! No extra flags.
ResolvePointer = 1 << 0, //! The pointer passed in contains a pointer. The (de)serializer will attempt to resolve to an instance.
ReplaceDefault = 1 << 1, //! The default value provided for storing will be replaced with a newly created one.
LoadAsNewInstance = 1 << 2 //! Treats the value as if it's a newly created instance. This may trigger serializers marked with
//! OperationFlags::InitializeNewInstance. Used for instance by pointers or new instances added to
//! an array.
};
enum class OperationFlags
{
None = 0, //! No flags that control how the custom json serializer is used.
ManualDefault = 1 << 0 //! Even if an (explicit) default is found the custom json serializer will still be called.
None = 0, //! No flags that control how the custom json serializer is used.
ManualDefault = 1 << 0, //! Even if an (explicit) default is found the custom json serializer will still be called.
InitializeNewInstance = 1 << 1 //! If set, the custom json serializer will be called with an explicit default if a new
//! instance of its target type is created.
};
virtual ~BaseJsonSerializer() = default;
@@ -34,11 +34,16 @@ namespace AZ
case rapidjson::kArrayType:
return LoadContainer(outputValue, outputValueTypeId, inputValue, context);
case rapidjson::kObjectType: // fall through
case rapidjson::kNullType: // fall through
case rapidjson::kStringType: // fall through
case rapidjson::kFalseType: // fall through
case rapidjson::kTrueType: // fall through
case rapidjson::kObjectType:
[[fallthrough]];
case rapidjson::kNullType:
[[fallthrough]];
case rapidjson::kStringType:
[[fallthrough]];
case rapidjson::kFalseType:
[[fallthrough]];
case rapidjson::kTrueType:
[[fallthrough]];
case rapidjson::kNumberType:
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Unsupported type. Basic containers can only be read from an array.");
@@ -123,6 +128,10 @@ namespace AZ
{
if (retVal.HasDoneWork())
{
// If at least one value was written, even if it has all defaults, then the array has
// a value written to it and is therefore not in a default state anymore.
retVal.Combine(JSR::ResultCode(JSR::Tasks::WriteValue, JSR::Outcomes::Success));
outputValue = AZStd::move(array);
return context.Report(retVal, "Content written to basic container.");
}
@@ -165,6 +174,7 @@ namespace AZ
ContinuationFlags flags = classElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER
? ContinuationFlags::ResolvePointer
: ContinuationFlags::None;
flags |= ContinuationFlags::LoadAsNewInstance;
const size_t capacity = container->IsFixedCapacity() ? container->Capacity(outputValue) : std::numeric_limits<size_t>::max();
@@ -243,6 +253,11 @@ namespace AZ
}
size_t addedCount = container->Size(outputValue) - containerSize;
if (addedCount > 0)
{
// Values were added which means the container is no longer in its default state of being empty.
retVal.Combine(JSR::ResultCode(JSR::Tasks::ReadField, JSR::Outcomes::Success));
}
AZStd::string_view message =
addedCount >= arraySize ? "Successfully read basic container.":
addedCount == 0 ? "Unable to read data for basic container." :
@@ -82,12 +82,18 @@ namespace AZ
bool* valAsBool = reinterpret_cast<bool*>(outputValue);
if (IsExplicitDefault(inputValue))
{
*valAsBool = {};
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed, "Boolean value set to default of 'false'.");
}
switch (inputValue.GetType())
{
case rapidjson::kArrayType:
// fallthrough
[[fallthrough]];
case rapidjson::kObjectType:
// fallthrough
[[fallthrough]];
case rapidjson::kNullType:
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Unsupported type. Booleans can't be read from arrays, objects or null.");
@@ -96,7 +102,7 @@ namespace AZ
return SerializerInternal::TextToValue(valAsBool, inputValue.GetString(), inputValue.GetStringLength(), context);
case rapidjson::kFalseType:
// fallthrough
[[fallthrough]];
case rapidjson::kTrueType:
*valAsBool = inputValue.GetBool();
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Success, "Successfully read boolean.");
@@ -145,4 +151,9 @@ namespace AZ
return context.Report(JSR::Tasks::WriteValue, JSR::Outcomes::DefaultsUsed, "Default boolean used.");
}
auto JsonBoolSerializer::GetOperationsFlags() const -> OperationFlags
{
return OperationFlags::InitializeNewInstance;
}
} // namespace AZ
@@ -27,5 +27,6 @@ namespace AZ
JsonDeserializerContext& context) override;
JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
const Uuid& valueTypeId, JsonSerializerContext& context) override;
OperationFlags GetOperationsFlags() const override;
};
} // namespace AZ
@@ -62,19 +62,26 @@ namespace AZ
}
template <typename T>
static JsonSerializationResult::Result Load(T* outputValue, const rapidjson::Value& inputValue, JsonDeserializerContext& context)
static JsonSerializationResult::Result Load(
T* outputValue, const rapidjson::Value& inputValue, JsonDeserializerContext& context, bool isExplicitDefault)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
static_assert(AZStd::is_floating_point<T>::value, "Expected T to be a floating point type");
AZ_Assert(outputValue, "Expected a valid pointer to load from json value.");
if (isExplicitDefault)
{
*outputValue = {};
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed, "Floating point value set to default of 0.0.");
}
switch (inputValue.GetType())
{
case rapidjson::kArrayType:
// fallthrough
[[fallthrough]];
case rapidjson::kObjectType:
// fallthrough
[[fallthrough]];
case rapidjson::kNullType:
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Unsupported type. Floating point values can't be read from arrays, objects or null.");
@@ -83,7 +90,7 @@ namespace AZ
return TextToValue(outputValue, inputValue.GetString(), context);
case rapidjson::kFalseType:
// fallthrough
[[fallthrough]];
case rapidjson::kTrueType:
*outputValue = inputValue.GetBool() ? 1.0f : 0.0f;
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Success,
@@ -144,7 +151,8 @@ namespace AZ
"Unable to deserialize double to json because the provided type is %s",
outputValueTypeId.ToString<AZStd::string>().c_str());
AZ_UNUSED(outputValueTypeId);
return SerializerFloatingPointInternal::Load(reinterpret_cast<double*>(outputValue), inputValue, context);
return SerializerFloatingPointInternal::Load(
reinterpret_cast<double*>(outputValue), inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonDoubleSerializer::Store(rapidjson::Value& outputValue, const void* inputValue,
@@ -156,6 +164,11 @@ namespace AZ
return SerializerFloatingPointInternal::Store<double>(outputValue, inputValue, defaultValue, context);
}
auto JsonDoubleSerializer::GetOperationsFlags() const -> OperationFlags
{
return OperationFlags::InitializeNewInstance;
}
JsonSerializationResult::Result JsonFloatSerializer::Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context)
{
@@ -163,7 +176,8 @@ namespace AZ
"Unable to deserialize float to json because the provided type is %s",
outputValueTypeId.ToString<AZStd::string>().c_str());
AZ_UNUSED(outputValueTypeId);
return SerializerFloatingPointInternal::Load(reinterpret_cast<float*>(outputValue), inputValue, context);
return SerializerFloatingPointInternal::Load(
reinterpret_cast<float*>(outputValue), inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonFloatSerializer::Store(rapidjson::Value& outputValue, const void* inputValue,
@@ -174,4 +188,9 @@ namespace AZ
AZ_UNUSED(valueTypeId);
return SerializerFloatingPointInternal::Store<float>(outputValue, inputValue, defaultValue, context);
}
auto JsonFloatSerializer::GetOperationsFlags() const -> OperationFlags
{
return OperationFlags::InitializeNewInstance;
}
} // namespace AZ
@@ -28,6 +28,7 @@ namespace AZ
JsonDeserializerContext& context) override;
JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
const Uuid& valueTypeId, JsonSerializerContext& context) override;
OperationFlags GetOperationsFlags() const override;
};
class JsonFloatSerializer
@@ -40,5 +41,6 @@ namespace AZ
JsonDeserializerContext& context) override;
JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
const Uuid& valueTypeId, JsonSerializerContext& context) override;
OperationFlags GetOperationsFlags() const override;
};
} // namespace AZ
@@ -25,6 +25,8 @@
namespace AZ
{
AZ_CLASS_ALLOCATOR_IMPL(BaseJsonIntegerSerializer, SystemAllocator, 0);
AZ_CLASS_ALLOCATOR_IMPL(JsonCharSerializer, SystemAllocator, 0);
AZ_CLASS_ALLOCATOR_IMPL(JsonShortSerializer, SystemAllocator, 0);
AZ_CLASS_ALLOCATOR_IMPL(JsonIntSerializer, SystemAllocator, 0);
@@ -56,19 +58,25 @@ namespace AZ
template <typename T>
static JsonSerializationResult::Result LoadInt(T* outputValue, const rapidjson::Value& inputValue,
JsonDeserializerContext& context)
JsonDeserializerContext& context, bool isDefaultValue)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
static_assert(AZStd::is_integral<T>(), "Expected T to be a signed or unsigned type");
AZ_Assert(outputValue, "Expected a valid pointer to load from json value.");
if (isDefaultValue)
{
*outputValue = {};
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed, "Integer value set to default of zero.");
}
switch (inputValue.GetType())
{
case rapidjson::kArrayType:
// fallthrough
[[fallthrough]];
case rapidjson::kObjectType:
// fallthrough
[[fallthrough]];
case rapidjson::kNullType:
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Unsupported type. Integers can't be read from arrays, objects or null.");
@@ -77,7 +85,7 @@ namespace AZ
return TextToValue(outputValue, inputValue.GetString(), context);
case rapidjson::kFalseType:
// fallthrough
[[fallthrough]];
case rapidjson::kTrueType:
*outputValue = inputValue.GetBool() ? 1 : 0;
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Success,
@@ -125,6 +133,11 @@ namespace AZ
}
} // namespace SerializerInternal
auto BaseJsonIntegerSerializer::GetOperationsFlags() const -> OperationFlags
{
return OperationFlags::InitializeNewInstance;
}
JsonSerializationResult::Result JsonCharSerializer::Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context)
{
@@ -132,7 +145,7 @@ namespace AZ
"Unable to deserialize char to json because the provided type is %s",
outputValueTypeId.ToString<AZStd::string>().c_str());
AZ_UNUSED(outputValueTypeId);
return SerializerInternal::LoadInt(reinterpret_cast<char*>(outputValue), inputValue, context);
return SerializerInternal::LoadInt(reinterpret_cast<char*>(outputValue), inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonCharSerializer::Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
@@ -151,7 +164,7 @@ namespace AZ
"Unable to deserialize short to json because the provided type is %s",
outputValueTypeId.ToString<AZStd::string>().c_str());
AZ_UNUSED(outputValueTypeId);
return SerializerInternal::LoadInt(reinterpret_cast<short*>(outputValue), inputValue, context);
return SerializerInternal::LoadInt(reinterpret_cast<short*>(outputValue), inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonShortSerializer::Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
@@ -170,7 +183,7 @@ namespace AZ
"Unable to deserialize int to json because the provided type is %s",
outputValueTypeId.ToString<AZStd::string>().c_str());
AZ_UNUSED(outputValueTypeId);
return SerializerInternal::LoadInt(reinterpret_cast<int*>(outputValue), inputValue, context);
return SerializerInternal::LoadInt(reinterpret_cast<int*>(outputValue), inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonIntSerializer::Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
@@ -189,7 +202,7 @@ namespace AZ
"Unable to deserialize long to json because the provided type is %s",
outputValueTypeId.ToString<AZStd::string>().c_str());
AZ_UNUSED(outputValueTypeId);
return SerializerInternal::LoadInt(reinterpret_cast<long*>(outputValue), inputValue, context);
return SerializerInternal::LoadInt(reinterpret_cast<long*>(outputValue), inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonLongSerializer::Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
@@ -208,7 +221,7 @@ namespace AZ
"Unable to deserialize long long to json because the provided type is %s",
outputValueTypeId.ToString<AZStd::string>().c_str());
AZ_UNUSED(outputValueTypeId);
return SerializerInternal::LoadInt(reinterpret_cast<long long*>(outputValue), inputValue, context);
return SerializerInternal::LoadInt(reinterpret_cast<long long*>(outputValue), inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonLongLongSerializer::Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
@@ -227,7 +240,8 @@ namespace AZ
"Unable to deserialize unsigned char to json because the provided type is %s",
outputValueTypeId.ToString<AZStd::string>().c_str());
AZ_UNUSED(outputValueTypeId);
return SerializerInternal::LoadInt(reinterpret_cast<unsigned char*>(outputValue), inputValue, context);
return SerializerInternal::LoadInt(
reinterpret_cast<unsigned char*>(outputValue), inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonUnsignedCharSerializer::Store(rapidjson::Value& outputValue, const void* inputValue,
@@ -246,7 +260,8 @@ namespace AZ
"Unable to deserialize unsigned short to json because the provided type is %s",
outputValueTypeId.ToString<AZStd::string>().c_str());
AZ_UNUSED(outputValueTypeId);
return SerializerInternal::LoadInt(reinterpret_cast<unsigned short*>(outputValue), inputValue, context);
return SerializerInternal::LoadInt(
reinterpret_cast<unsigned short*>(outputValue), inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonUnsignedShortSerializer::Store(rapidjson::Value& outputValue, const void* inputValue,
@@ -265,7 +280,8 @@ namespace AZ
"Unable to deserialize unsigned int to json because the provided type is %s",
outputValueTypeId.ToString<AZStd::string>().c_str());
AZ_UNUSED(outputValueTypeId);
return SerializerInternal::LoadInt(reinterpret_cast<unsigned int*>(outputValue), inputValue, context);
return SerializerInternal::LoadInt(
reinterpret_cast<unsigned int*>(outputValue), inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonUnsignedIntSerializer::Store(rapidjson::Value& outputValue, const void* inputValue,
@@ -284,7 +300,8 @@ namespace AZ
"Unable to deserialize unsigned long to json because the provided type is %s",
outputValueTypeId.ToString<AZStd::string>().c_str());
AZ_UNUSED(outputValueTypeId);
return SerializerInternal::LoadInt(reinterpret_cast<unsigned long*>(outputValue), inputValue, context);
return SerializerInternal::LoadInt(
reinterpret_cast<unsigned long*>(outputValue), inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonUnsignedLongSerializer::Store(rapidjson::Value& outputValue, const void* inputValue,
@@ -303,7 +320,8 @@ namespace AZ
"Unable to deserialize unsigned long long to json because the provided type is %s",
outputValueTypeId.ToString<AZStd::string>().c_str());
AZ_UNUSED(outputValueTypeId);
return SerializerInternal::LoadInt(reinterpret_cast<unsigned long long*>(outputValue), inputValue, context);
return SerializerInternal::LoadInt(
reinterpret_cast<unsigned long long*>(outputValue), inputValue, context, IsExplicitDefault(inputValue));
}
JsonSerializationResult::Result JsonUnsignedLongLongSerializer::Store(rapidjson::Value& outputValue, const void* inputValue,
@@ -18,11 +18,18 @@
namespace AZ
{
class JsonCharSerializer
: public BaseJsonSerializer
class BaseJsonIntegerSerializer : public BaseJsonSerializer
{
public:
AZ_RTTI(JsonCharSerializer, "{CA2A4AAC-3068-40B2-94F8-A537FBA8236E}", BaseJsonSerializer);
AZ_RTTI(BaseJsonIntegerSerializer, "{FD060F54-D3B5-4D5B-B64A-AFE371CD6F20}", BaseJsonSerializer);
AZ_CLASS_ALLOCATOR_DECL;
OperationFlags GetOperationsFlags() const override;
};
class JsonCharSerializer : public BaseJsonIntegerSerializer
{
public:
AZ_RTTI(JsonCharSerializer, "{CA2A4AAC-3068-40B2-94F8-A537FBA8236E}", BaseJsonIntegerSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -30,11 +37,10 @@ namespace AZ
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
class JsonShortSerializer
: public BaseJsonSerializer
class JsonShortSerializer : public BaseJsonIntegerSerializer
{
public:
AZ_RTTI(JsonShortSerializer, "{3D6789BD-231B-4E5D-B81D-609E71A2BCB5}", BaseJsonSerializer);
AZ_RTTI(JsonShortSerializer, "{3D6789BD-231B-4E5D-B81D-609E71A2BCB5}", BaseJsonIntegerSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -42,11 +48,10 @@ namespace AZ
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
class JsonIntSerializer
: public BaseJsonSerializer
class JsonIntSerializer : public BaseJsonIntegerSerializer
{
public:
AZ_RTTI(JsonIntSerializer, "{29E26946-0F1F-44B0-A098-1171B7B0C8FA}", BaseJsonSerializer);
AZ_RTTI(JsonIntSerializer, "{29E26946-0F1F-44B0-A098-1171B7B0C8FA}", BaseJsonIntegerSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -54,11 +59,10 @@ namespace AZ
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
class JsonLongSerializer
: public BaseJsonSerializer
class JsonLongSerializer : public BaseJsonIntegerSerializer
{
public:
AZ_RTTI(JsonLongSerializer, "{0EB432D0-A0C8-43B2-9D65-A73A4D6DFE3E}", BaseJsonSerializer);
AZ_RTTI(JsonLongSerializer, "{0EB432D0-A0C8-43B2-9D65-A73A4D6DFE3E}", BaseJsonIntegerSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -66,11 +70,10 @@ namespace AZ
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
class JsonLongLongSerializer
: public BaseJsonSerializer
class JsonLongLongSerializer : public BaseJsonIntegerSerializer
{
public:
AZ_RTTI(JsonLongLongSerializer, "{5E7967DE-A4DC-40E1-81A1-2896A054BB8A}", BaseJsonSerializer);
AZ_RTTI(JsonLongLongSerializer, "{5E7967DE-A4DC-40E1-81A1-2896A054BB8A}", BaseJsonIntegerSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -78,11 +81,10 @@ namespace AZ
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
class JsonUnsignedCharSerializer
: public BaseJsonSerializer
class JsonUnsignedCharSerializer : public BaseJsonIntegerSerializer
{
public:
AZ_RTTI(JsonUnsignedCharSerializer, "{1E6D606F-8490-4736-AAFF-91046FDEA2BB}", BaseJsonSerializer);
AZ_RTTI(JsonUnsignedCharSerializer, "{1E6D606F-8490-4736-AAFF-91046FDEA2BB}", BaseJsonIntegerSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -90,11 +92,10 @@ namespace AZ
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
class JsonUnsignedShortSerializer
: public BaseJsonSerializer
class JsonUnsignedShortSerializer : public BaseJsonIntegerSerializer
{
public:
AZ_RTTI(JsonUnsignedShortSerializer, "{3C92D2CC-CB13-4A40-B779-47562EE36451}", BaseJsonSerializer);
AZ_RTTI(JsonUnsignedShortSerializer, "{3C92D2CC-CB13-4A40-B779-47562EE36451}", BaseJsonIntegerSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -102,11 +103,10 @@ namespace AZ
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
class JsonUnsignedIntSerializer
: public BaseJsonSerializer
class JsonUnsignedIntSerializer : public BaseJsonIntegerSerializer
{
public:
AZ_RTTI(JsonUnsignedIntSerializer, "{70C0714A-690D-4F30-8986-ABC9DEFE9D62}", BaseJsonSerializer);
AZ_RTTI(JsonUnsignedIntSerializer, "{70C0714A-690D-4F30-8986-ABC9DEFE9D62}", BaseJsonIntegerSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -114,11 +114,10 @@ namespace AZ
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
class JsonUnsignedLongSerializer
: public BaseJsonSerializer
class JsonUnsignedLongSerializer : public BaseJsonIntegerSerializer
{
public:
AZ_RTTI(JsonUnsignedLongSerializer, "{28E5499F-6AF4-4778-AE14-66BA40B56247}", BaseJsonSerializer);
AZ_RTTI(JsonUnsignedLongSerializer, "{28E5499F-6AF4-4778-AE14-66BA40B56247}", BaseJsonIntegerSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -126,11 +125,10 @@ namespace AZ
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
class JsonUnsignedLongLongSerializer
: public BaseJsonSerializer
class JsonUnsignedLongLongSerializer : public BaseJsonIntegerSerializer
{
public:
AZ_RTTI(JsonUnsignedLongLongSerializer, "{AB048BB3-C280-4166-9E2E-54CE2C3413CA}", BaseJsonSerializer);
AZ_RTTI(JsonUnsignedLongLongSerializer, "{AB048BB3-C280-4166-9E2E-54CE2C3413CA}", BaseJsonIntegerSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
@@ -19,25 +19,30 @@
#include <AzCore/Serialization/Json/RegistrationContext.h>
#include <AzCore/Serialization/Json/StackedString.h>
#include <AzCore/std/string/conversions.h>
#include <AzCore/std/string/fixed_string.h>
#include <AzCore/std/string/string.h>
namespace AZ
{
JsonSerializationResult::ResultCode JsonDeserializer::DeserializerDefaultCheck(BaseJsonSerializer* serializer, void* object,
const Uuid& typeId, const rapidjson::Value& value, JsonDeserializerContext& context)
const Uuid& typeId,const rapidjson::Value& value, bool isNewInstance, JsonDeserializerContext& context)
{
using namespace AZ::JsonSerializationResult;
bool isExplicitDefault = IsExplicitDefault(value);
bool manuallyDefaults = (serializer->GetOperationsFlags() & BaseJsonSerializer::OperationFlags::ManualDefault) ==
BaseJsonSerializer::OperationFlags::ManualDefault;
return !isExplicitDefault || (isExplicitDefault && manuallyDefaults)
bool initializeNewInstance = (serializer->GetOperationsFlags() & BaseJsonSerializer::OperationFlags::InitializeNewInstance) ==
BaseJsonSerializer::OperationFlags::InitializeNewInstance;
return
!isExplicitDefault || (isExplicitDefault && manuallyDefaults) || (isExplicitDefault && isNewInstance && initializeNewInstance)
? serializer->Load(object, typeId, value, context)
: context.Report(Tasks::ReadField, Outcomes::DefaultsUsed, "Value has an explicit default.");
}
JsonSerializationResult::ResultCode JsonDeserializer::Load(void* object, const Uuid& typeId, const rapidjson::Value& value,
JsonDeserializerContext& context)
JsonSerializationResult::ResultCode JsonDeserializer::Load(
void* object, const Uuid& typeId, const rapidjson::Value& value, bool isNewInstance, JsonDeserializerContext& context)
{
using namespace AZ::JsonSerializationResult;
@@ -50,7 +55,7 @@ namespace AZ
BaseJsonSerializer* serializer = context.GetRegistrationContext()->GetSerializerForType(typeId);
if (serializer)
{
return DeserializerDefaultCheck(serializer, object, typeId, value, context);
return DeserializerDefaultCheck(serializer, object, typeId, value, isNewInstance, context);
}
const SerializeContext::ClassData* classData = context.GetSerializeContext()->FindClassData(typeId);
@@ -72,7 +77,7 @@ namespace AZ
serializer = context.GetRegistrationContext()->GetSerializerForType(classData->m_azRtti->GetGenericTypeId());
if (serializer)
{
return DeserializerDefaultCheck(serializer, object, typeId, value, context);
return DeserializerDefaultCheck(serializer, object, typeId, value, isNewInstance, context);
}
}
@@ -133,7 +138,7 @@ namespace AZ
const SerializeContext::ClassData* resolvedClassData = context.GetSerializeContext()->FindClassData(resolvedTypeId);
if (resolvedClassData)
{
status = JsonDeserializer::Load(*objectPtr, resolvedTypeId, value, context);
status = JsonDeserializer::Load(*objectPtr, resolvedTypeId, value, true, context);
*objectPtr = resolvedClassData->m_azRtti->Cast(*objectPtr, typeId);
@@ -174,7 +179,7 @@ namespace AZ
}
else
{
return Load(object, classElement.m_typeId, value, context);
return Load(object, classElement.m_typeId, value, false, context);
}
}
@@ -591,7 +596,9 @@ namespace AZ
}
else
{
status = context.Report(Tasks::RetrieveInfo, Outcomes::Unknown, "Serialization information for target type not found.");
using ReporterString = AZStd::fixed_string<1024>;
status = context.Report(Tasks::RetrieveInfo, Outcomes::Unknown,
ReporterString::format("Serialization information for target type %s not found.", loadedTypeId.m_typeId.ToString<ReporterString>().c_str()));
return ResolvePointerResult::FullyProcessed;
}
objectType = loadedTypeId.m_typeId;
@@ -58,8 +58,8 @@ namespace AZ
JsonDeserializer(const JsonDeserializer& rhs) = delete;
JsonDeserializer(JsonDeserializer&& rhs) = delete;
static JsonSerializationResult::ResultCode Load(void* object, const Uuid& typeId, const rapidjson::Value& value,
JsonDeserializerContext& context);
static JsonSerializationResult::ResultCode Load(
void* object, const Uuid& typeId, const rapidjson::Value& value, bool isNewInstance, JsonDeserializerContext& context);
static JsonSerializationResult::ResultCode LoadToPointer(void* object, const Uuid& typeId, const rapidjson::Value& value,
JsonDeserializerContext& context);
@@ -120,6 +120,7 @@ namespace AZ
void* object,
const Uuid& typeId,
const rapidjson::Value& value,
bool isNewInstance,
JsonDeserializerContext& context);
};
} // namespace AZ
@@ -249,7 +249,7 @@ namespace AZ
{
StackedString path(StackedString::Format::JsonPointer);
JsonDeserializerContext context(settings);
result = JsonDeserializer::Load(object, objectType, root, context);
result = JsonDeserializer::Load(object, objectType, root, false, context);
}
return result;
}
@@ -190,6 +190,12 @@ namespace AZ
}
size_t addedCount = container->Size(outputValue) - containerSize;
if (addedCount > 0)
{
// If at least one entry was added then the map is no longer in it's default state so
// mark is with success so the result can at best be partial defaults.
retVal.Combine(JSR::ResultCode(JSR::Tasks::ReadField, JSR::Outcomes::Success));
}
AZStd::string_view message =
addedCount >= maximumSize ? "Successfully read associative container." :
addedCount == 0 ? "Unable to read data for the associative container." :
@@ -215,10 +221,10 @@ namespace AZ
// Load key
void* keyAddress = pairContainer->GetElementByIndex(address, pairElement, 0);
AZ_Assert(keyAddress, "Element reserved for associative container, but unable to retrieve address of the key.");
ContinuationFlags keyLoadFlags = ContinuationFlags::None;
ContinuationFlags keyLoadFlags = ContinuationFlags::LoadAsNewInstance;
if (keyElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER)
{
keyLoadFlags = ContinuationFlags::ResolvePointer;
keyLoadFlags |= ContinuationFlags::ResolvePointer;
*reinterpret_cast<void**>(keyAddress) = nullptr;
}
JSR::ResultCode keyResult = ContinueLoading(keyAddress, keyElement->m_typeId, key, context, keyLoadFlags);
@@ -231,10 +237,10 @@ namespace AZ
// Load value
void* valueAddress = pairContainer->GetElementByIndex(address, pairElement, 1);
AZ_Assert(valueAddress, "Element reserved for associative container, but unable to retrieve address of the value.");
ContinuationFlags valueLoadFlags = ContinuationFlags::None;
ContinuationFlags valueLoadFlags = ContinuationFlags::LoadAsNewInstance;
if (valueElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER)
{
valueLoadFlags = ContinuationFlags::ResolvePointer;
valueLoadFlags |= ContinuationFlags::ResolvePointer;
*reinterpret_cast<void**>(valueAddress) = nullptr;
}
JSR::ResultCode valueResult = ContinueLoading(valueAddress, valueElement->m_typeId, value, context, valueLoadFlags);
@@ -34,13 +34,24 @@ namespace AZ
switch (inputValue.GetType())
{
case rapidjson::kArrayType:
return LoadContainer(outputValue, outputValueTypeId, inputValue, context);
return LoadContainer(outputValue, outputValueTypeId, inputValue, false, context);
case rapidjson::kObjectType: // fall through
case rapidjson::kNullType: // fall through
case rapidjson::kStringType: // fall through
case rapidjson::kFalseType: // fall through
case rapidjson::kTrueType: // fall through
case rapidjson::kObjectType:
if (IsExplicitDefault(inputValue))
{
// Because this serializer has only the operation flag "InitializeNewInstance" set, the only time this will be called with
// an explicit default is when a new instance has been created.
return LoadContainer(outputValue, outputValueTypeId, inputValue, true, context);
}
[[fallthrough]];
case rapidjson::kNullType:
[[fallthrough]];
case rapidjson::kStringType:
[[fallthrough]];
case rapidjson::kFalseType:
[[fallthrough]];
case rapidjson::kTrueType:
[[fallthrough]];
case rapidjson::kNumberType:
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Unsupported type. AZStd::pair or AZStd::tuple can only be read from an array.");
@@ -127,8 +138,13 @@ namespace AZ
}
}
auto JsonTupleSerializer::GetOperationsFlags() const -> OperationFlags
{
return OperationFlags::InitializeNewInstance;
}
JsonSerializationResult::Result JsonTupleSerializer::LoadContainer(void* outputValue, const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
const rapidjson::Value& inputValue, bool isNewInstance, JsonDeserializerContext& context)
{
namespace JSR = JsonSerializationResult; // Used to remove name conflicts in AzCore in uber builds.
@@ -154,13 +170,6 @@ namespace AZ
};
container->EnumTypes(typeCountCallback);
rapidjson::SizeType arraySize = inputValue.Size();
if (arraySize < typeCount)
{
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Not enough entries in array to load an AZStd::pair or AZStd::tuple from.");
}
AZStd::vector<const SerializeContext::ClassElement*> classElements;
classElements.reserve(typeCount);
auto typeEnumCallback = [&classElements](const Uuid&, const SerializeContext::ClassElement* genericClassElement)
@@ -171,46 +180,80 @@ namespace AZ
container->EnumTypes(typeEnumCallback);
JSR::ResultCode retVal(JSR::Tasks::ReadField);
rapidjson::SizeType arrayIndex = 0;
size_t numElementsWritten = 0;
for (size_t i = 0; i < typeCount; ++i)
if (isNewInstance)
{
ScopedContextPath subPath(context, i);
void* elementAddress = container->GetElementByIndex(outputValue, nullptr, i);
AZ_Assert(elementAddress, "Address of AZStd::pair or AZStd::tuple element %zu could not be retrieved.", i);
rapidjson::Value explicitDefaultValue = GetExplicitDefault();
ContinuationFlags flags = classElements[i]->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER
? ContinuationFlags::ResolvePointer
: ContinuationFlags::None;
while (arrayIndex < inputValue.Size())
for (size_t i = 0; i < typeCount; ++i)
{
JSR::ResultCode result = ContinueLoading(elementAddress, classElements[i]->m_typeId, inputValue[arrayIndex], context, flags);
ScopedContextPath subPath(context, i);
void* elementAddress = container->GetElementByIndex(outputValue, nullptr, i);
AZ_Assert(elementAddress, "Address of AZStd::pair or AZStd::tuple element %zu could not be retrieved.", i);
ContinuationFlags flags = ContinuationFlags::LoadAsNewInstance;
flags |=
(classElements[i]->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER ? ContinuationFlags::ResolvePointer
: ContinuationFlags::None);
JSR::ResultCode result = ContinueLoading(elementAddress, classElements[i]->m_typeId, explicitDefaultValue, context, flags);
retVal.Combine(result);
arrayIndex++;
if (result.GetProcessing() == JSR::Processing::Halted)
{
return context.Report(retVal, "Failed to read element for AZStd::pair or AZStd::tuple.");
}
else if (result.GetProcessing() != JSR::Processing::Altered)
{
numElementsWritten++;
break;
}
}
}
if (numElementsWritten < typeCount)
{
AZStd::string_view message = numElementsWritten == 0 ?
"Unable to read data for AZStd::pair or AZStd::tuple." :
"Partially read data for AZStd::pair or AZStd::tuple.";
return context.Report(retVal, message);
return context.Report(retVal, "Initialized AZStd::pair or AZStd::tuple to defaults.");
}
else
{
return context.Report(retVal, "Successfully read AZStd::pair or AZStd::tuple.");
if (inputValue.Size() < typeCount)
{
return context.Report(
JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Not enough entries in array to load an AZStd::pair or AZStd::tuple from.");
}
rapidjson::SizeType arrayIndex = 0;
size_t numElementsWritten = 0;
for (size_t i = 0; i < typeCount; ++i)
{
ScopedContextPath subPath(context, i);
void* elementAddress = container->GetElementByIndex(outputValue, nullptr, i);
AZ_Assert(elementAddress, "Address of AZStd::pair or AZStd::tuple element %zu could not be retrieved.", i);
ContinuationFlags flags =
(classElements[i]->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER ? ContinuationFlags::ResolvePointer
: ContinuationFlags::None);
while (arrayIndex < inputValue.Size())
{
JSR::ResultCode result =
ContinueLoading(elementAddress, classElements[i]->m_typeId, inputValue[arrayIndex], context, flags);
retVal.Combine(result);
arrayIndex++;
if (result.GetProcessing() == JSR::Processing::Halted)
{
return context.Report(retVal, "Failed to read element for AZStd::pair or AZStd::tuple.");
}
else if (result.GetProcessing() != JSR::Processing::Altered)
{
numElementsWritten++;
break;
}
}
}
if (numElementsWritten < typeCount)
{
AZStd::string_view message = numElementsWritten == 0 ? "Unable to read data for AZStd::pair or AZStd::tuple."
: "Partially read data for AZStd::pair or AZStd::tuple.";
return context.Report(retVal, message);
}
else
{
return context.Report(retVal, "Successfully read AZStd::pair or AZStd::tuple.");
}
}
}
} // namespace AZ
@@ -23,13 +23,20 @@ namespace AZ
public:
AZ_RTTI(JsonTupleSerializer, "{1AA0ADC1-395A-4223-8A73-304ACDEE7793}", BaseJsonSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
const Uuid& valueTypeId, JsonSerializerContext& context) override;
OperationFlags GetOperationsFlags() const override;
private:
JsonSerializationResult::Result LoadContainer(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonSerializationResult::Result LoadContainer(
void* outputValue,
const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue,
bool isNewInstance,
JsonDeserializerContext& context);
};
} // namespace AZ
@@ -816,7 +816,11 @@ namespace AZ::SettingsRegistryMergeUtils
}
}
void MergeSettingsToRegistry_CommandLine(SettingsRegistryInterface& registry, const AZ::CommandLine& commandLine, bool executeCommands)
// This function intentionally copies `commandLine`. It looks like it only uses it as a const reference, but the
// code in the loop makes calls that mutates the `commandLine` instance, invalidating the iterators. Making a copy
// ensures that the iterators remain valid.
// NOLINTNEXTLINE(performance-unnecessary-value-param)
void MergeSettingsToRegistry_CommandLine(SettingsRegistryInterface& registry, AZ::CommandLine commandLine, bool executeCommands)
{
// Iterate over all the command line options in order to parse the --regset and --regremove
// arguments in the order they were supplied
@@ -831,7 +835,7 @@ namespace AZ::SettingsRegistryMergeUtils
continue;
}
}
if (commandArgument.m_option == "regremove")
else if (commandArgument.m_option == "regremove")
{
if (!registry.Remove(commandArgument.m_value))
{
@@ -15,11 +15,7 @@
#include <AzCore/IO/Path/Path_fwd.h>
#include <AzCore/Memory/OSAllocator.h>
#include <AzCore/Settings/SettingsRegistry.h>
namespace AZ
{
class CommandLine;
}
#include <AzCore/Settings/CommandLine.h>
namespace AZ::IO
{
@@ -217,7 +213,7 @@ namespace AZ::SettingsRegistryMergeUtils
//! example: --regdump /My/Array/With/Objects
//! --regdumpall Dumps the entire settings registry to output.
//! Note that this function is only called in development builds and is compiled out in release builds.
void MergeSettingsToRegistry_CommandLine(SettingsRegistryInterface& registry, const AZ::CommandLine& commandLine, bool executeCommands);
void MergeSettingsToRegistry_CommandLine(SettingsRegistryInterface& registry, AZ::CommandLine commandLine, bool executeCommands);
//! Stores the command line settings into the Setting Registry
//! The arguments can be used later anywhere the command line is needed
@@ -168,6 +168,10 @@ namespace JsonSerializationTests
{
features.EnableJsonType(rapidjson::kObjectType);
features.m_typeToInject = rapidjson::kNullType;
// Assets are not fully registered with the Serialize Context for historical reasons. Due to the missing
// information the Json Serializer Conformity Tests can't run the subsection of tests that explicitly
// require the missing information.
features.m_enableNewInstanceTests = false;
}
bool AreEqual(const Asset& lhs, const Asset& rhs) override
@@ -32,6 +32,11 @@ namespace JsonSerializationTests
AZ::NameDictionary::Destroy();
}
void Reflect(AZStd::unique_ptr<AZ::SerializeContext>& context)
{
AZ::Name::Reflect(context.get());
}
void Reflect(AZStd::unique_ptr<AZ::JsonRegistrationContext>& context)
{
AZ::Name::Reflect(context.get());
@@ -126,9 +126,9 @@ namespace JsonSerializationTests
{
auto array = AZStd::shared_ptr<Array>(new Array(), Deleter);
(*array)[0] = nullptr;
(*array)[1] = aznew MultipleInheritence();
(*array)[1] = nullptr;
(*array)[2] = nullptr;
(*array)[3] = aznew MultipleInheritence();
(*array)[3] = nullptr;
return array;
}
@@ -154,6 +154,7 @@ namespace JsonSerializationTests
null,
null,
{
"$type": "MultipleInheritence",
"base_var": 242.0,
"var1" : 142
}
@@ -246,56 +247,21 @@ namespace JsonSerializationTests
])";
}
AZStd::string_view GetJsonFor_Store_SerializeFullySetInstance() override
{
// This is a unique situation because the $type is determined separate from other values, so all
// member values can be changed, but since the default type matches the stored type the $type
// will only be written if default values are explicitly kept.
return R"(
[
{
"$type": "MultipleInheritence",
"base_var": 1142.0,
"base2_var1": 1242.0,
"base2_var2": 1342.0,
"base2_var3": 1442.0,
"var1" : 1542,
"var2" : 1642.0
},
{
"base_var": 2142.0,
"base2_var1": 2242.0,
"base2_var2": 2342.0,
"base2_var3": 2442.0,
"var1" : 2542,
"var2" : 2642.0
},
{
"$type": "MultipleInheritence",
"base_var": 3142.0,
"base2_var1": 3242.0,
"base2_var2": 3342.0,
"base2_var3": 3442.0,
"var1" : 3542,
"var2" : 3642.0
},
{
"base_var": 4142.0,
"base2_var1": 4242.0,
"base2_var2": 4342.0,
"base2_var3": 4442.0,
"var1" : 4542,
"var2" : 4642.0
}
])";
}
void Reflect(AZStd::unique_ptr<AZ::SerializeContext>& context) override
{
Base::Reflect(context);
MultipleInheritence::Reflect(context, true);
}
void ConfigureFeatures(JsonSerializerConformityTestDescriptorFeatures& features) override
{
Base::ConfigureFeatures(features);
// These tests don't work with pointers because there'll be a random value in the pointer
// which the Json Serialization will try to delete. The POD version of these tests already cover
// these cases.
features.m_enableNewInstanceTests = false;
}
bool AreEqual(const Array& lhs, const Array& rhs) override
{
size_t size = lhs.size();
@@ -311,6 +277,11 @@ namespace JsonSerializationTests
return rhs[i] == nullptr;
}
if (rhs[i] == nullptr)
{
return false;
}
if (!static_cast<const MultipleInheritence*>(lhs[i])->Equals(*static_cast<const MultipleInheritence*>(rhs[i]), true))
{
return false;
@@ -54,6 +54,11 @@ namespace JsonSerializationTests
return AZStd::make_shared<Container>(Container{ 188, 288, 388 });
}
AZStd::shared_ptr<Container> CreateSingleArrayDefaultInstance() override
{
return AZStd::make_shared<Container>(Container{ 0 });
}
AZStd::string_view GetJsonForFullySetInstance() override
{
return "[188, 288, 388]";
@@ -120,6 +125,13 @@ namespace JsonSerializationTests
&SimplePointerTestDescription::Delete);
}
AZStd::shared_ptr<Container> CreateSingleArrayDefaultInstance() override
{
int* value = reinterpret_cast<int*>(azmalloc(sizeof(int), alignof(int)));
*value = 0;
return AZStd::shared_ptr<Container>(new Container{ value }, &SimplePointerTestDescription::Delete);
}
AZStd::string_view GetJsonForFullySetInstance() override
{
return "[188, 288, 388]";
@@ -180,6 +192,13 @@ namespace JsonSerializationTests
return instance;
}
AZStd::shared_ptr<Container> CreateSingleArrayDefaultInstance() override
{
auto instance = AZStd::make_shared<Container>();
*instance = { SimpleClass{} };
return instance;
}
AZStd::string_view GetJsonForFullySetInstance() override
{
return R"([
@@ -301,7 +320,7 @@ namespace JsonSerializationTests
ResultCode result = m_serializer->Store(*m_jsonDocument, &instance, &instance, azrtti_typeid(&instance), *m_jsonSerializationContext);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
EXPECT_EQ(Outcomes::DefaultsUsed, result.GetOutcome());
EXPECT_EQ(Outcomes::PartialDefaults, result.GetOutcome());
Expect_DocStrEq("[{}]");
}
@@ -315,7 +334,7 @@ namespace JsonSerializationTests
ResultCode result = m_serializer->Store(*m_jsonDocument, &instance, nullptr, azrtti_typeid(&instance), *m_jsonSerializationContext);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
EXPECT_EQ(Outcomes::DefaultsUsed, result.GetOutcome());
EXPECT_EQ(Outcomes::PartialDefaults, result.GetOutcome());
Expect_DocStrEq("[{},{}]");
}
@@ -330,7 +349,6 @@ namespace JsonSerializationTests
ResultCode result = m_serializer->Store(*m_jsonDocument, &instance, nullptr, azrtti_typeid(&instance), *m_jsonSerializationContext);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
EXPECT_EQ(Outcomes::PartialDefaults, result.GetOutcome());
EXPECT_NE(Outcomes::DefaultsUsed, result.GetOutcome());
Expect_DocStrEq(R"([{"$type": "SimpleInheritence"},{"$type": "SimpleInheritence"}])");
}
@@ -63,6 +63,18 @@ namespace JsonSerializationTests
: public BaseJsonSerializerFixture
{
public:
struct BoolPointerWrapper
{
AZ_TYPE_INFO(BoolPointerWrapper, "{2E67C069-BB0F-4F00-A704-E964F5FE5ED2}");
bool* m_value{ nullptr };
~BoolPointerWrapper()
{
azfree(m_value);
}
};
void SetUp() override
{
BaseJsonSerializerFixture::SetUp();
@@ -75,6 +87,12 @@ namespace JsonSerializationTests
BaseJsonSerializerFixture::TearDown();
}
void RegisterAdditional(AZStd::unique_ptr<AZ::SerializeContext>& serializeContext) override
{
serializeContext->Class<BoolPointerWrapper>()
->Field("Value", &BoolPointerWrapper::m_value);
}
void Load(rapidjson::Value& testVal, bool expectedBool, AZ::JsonSerializationResult::Outcomes expectedOutcome)
{
using namespace AZ::JsonSerializationResult;
@@ -32,7 +32,7 @@ namespace JsonSerializationTests
AZStd::shared_ptr<FloatingPointType> CreateDefaultInstance() override
{
return AZStd::make_shared<FloatingPointType>(-2.0f);
return AZStd::make_shared<FloatingPointType>(0.0f);
}
AZStd::shared_ptr<FloatingPointType> CreateFullySetInstance() override
@@ -71,6 +71,20 @@ namespace JsonSerializationTests
: public BaseJsonSerializerFixture
{
public:
struct DoublePointerWrapper
{
AZ_TYPE_INFO(DoublePointerWrapper, "{C2FD9E0B-2641-4D24-A3D9-A29FD1A21A81}");
double* m_double{ nullptr };
float* m_float{ nullptr };
~DoublePointerWrapper()
{
azfree(m_float);
azfree(m_double);
}
};
void SetUp() override
{
BaseJsonSerializerFixture::SetUp();
@@ -85,6 +99,13 @@ namespace JsonSerializationTests
BaseJsonSerializerFixture::TearDown();
}
void RegisterAdditional(AZStd::unique_ptr<AZ::SerializeContext>& serializeContext) override
{
serializeContext->Class<DoublePointerWrapper>()
->Field("Double", &DoublePointerWrapper::m_double)
->Field("Float", &DoublePointerWrapper::m_float);
}
void TestSerializers(rapidjson::Value& testVal, double expectedValue, AZ::JsonSerializationResult::Outcomes expectedOutcome)
{
using namespace AZ::JsonSerializationResult;
@@ -62,6 +62,14 @@ namespace JsonSerializationTests
//! can be used to manually create these documents. If that is also not an option the tests can be
//! disabled by setting this flag to false.
bool m_supportsInjection{ true };
//! Enables the check that tries to determine if variables are initialized and if not whether they have the
//! OperationFlags::ManualDefault set. This applies for instance to integers, which won't be initialized if
//! constructed a new instance is created for pointers.
bool m_enableInitializationTest{ true };
//! Enable the test that creates a new instance of the provided test type through the factory that's found in
//! the Serialize Context. This test is automatically disabled for classes that don't have a factory or
//! have a null factory as well as for classes that have mandatory fields.
bool m_enableNewInstanceTests{ true };
private:
// There's no way to retrieve the number of types from RapidJSON so they're hard-coded here.
@@ -87,6 +95,7 @@ namespace JsonSerializationTests
{
public:
using Type = T;
virtual ~JsonSerializerConformityTestDescriptor() = default;
virtual AZStd::shared_ptr<AZ::BaseJsonSerializer> CreateSerializer() = 0;
@@ -104,13 +113,21 @@ namespace JsonSerializationTests
virtual AZStd::shared_ptr<T> CreatePartialDefaultInstance() { return nullptr; }
//! Create an instance where all values are set to non-default values.
virtual AZStd::shared_ptr<T> CreateFullySetInstance() = 0;
//! Create an instance of the target array type with a single value that has all defaults.
//! If the target type doesn't support arrays or requires more than one entry this can be ignored and
//! tests using this value will be skipped.
virtual AZStd::shared_ptr<T> CreateSingleArrayDefaultInstance() { return nullptr; }
//! Get the json that represents the default instance.
//! If the target type doesn't support partial specialization this can be ignored and
//! tests for partial support will be skipped.
virtual AZStd::string_view GetJsonForPartialDefaultInstance() { return ""; }
virtual AZStd::string_view GetJsonForPartialDefaultInstance() { return ""; }
//! Get the json that represents the instance with all values set.
virtual AZStd::string_view GetJsonForFullySetInstance() = 0;
//! Get the json that represents an array with a single value that has only defaults.
//! If the target type doesn't support arrays or requires more than one entry this can be ignored and
//! tests using this value will be skipped.
virtual AZStd::string_view GetJsonForSingleArrayDefaultInstance() { return "[{}]"; }
//! Get the json where additional values are added to the json file.
//! If this function is not overloaded, but features.m_supportsInjection is enabled then
//! the Json Serializer Conformity Tests will inject extra values in the json for a fully.
@@ -138,12 +155,15 @@ namespace JsonSerializationTests
virtual AZStd::string_view GetJsonFor_Load_DeserializeUnreflectedType() { return this->GetJsonForFullySetInstance(); }
virtual AZStd::string_view GetJsonFor_Load_DeserializeFullySetInstance() { return this->GetJsonForFullySetInstance(); }
virtual AZStd::string_view GetJsonFor_Load_DeserializePartialInstance() { return this->GetJsonForPartialDefaultInstance(); }
virtual AZStd::string_view GetJsonFor_Load_DeserializeArrayWithDefaultValue() { return this->GetJsonForSingleArrayDefaultInstance(); }
virtual AZStd::string_view GetJsonFor_Load_DeserializeFullInstanceOnTopOfPartialDefaulted() { return this->GetJsonForFullySetInstance(); }
virtual AZStd::string_view GetJsonFor_Load_HaltedThroughCallback() { return this->GetJsonForFullySetInstance(); }
virtual AZStd::string_view GetJsonFor_Store_SerializeWithDefaultsKept() { return this->GetJsonForFullySetInstance(); }
virtual AZStd::string_view GetJsonFor_Store_SerializeFullySetInstance() { return this->GetJsonForFullySetInstance(); }
virtual AZStd::string_view GetJsonFor_Store_SerializeWithoutDefault() { return this->GetJsonForFullySetInstance(); }
virtual AZStd::string_view GetJsonFor_Store_SerializeWithoutDefaultAndDefaultsKept() { return this->GetJsonForFullySetInstance(); }
virtual AZStd::string_view GetJsonFor_Store_SerializePartialInstance() { return this->GetJsonForPartialDefaultInstance(); }
virtual AZStd::string_view GetJsonFor_Store_SerializeArrayWithSingleDefaultValue() { return this->GetJsonForSingleArrayDefaultInstance(); }
};
template<typename T>
@@ -154,6 +174,21 @@ namespace JsonSerializationTests
using Description = T;
using Type = typename T::Type;
struct PointerWrapper
{
AZ_TYPE_INFO(PointerWrapper, "{32FA6645-074A-458A-B79C-B173D0BD4B42}");
AZ_CLASS_ALLOCATOR(PointerWrapper, AZ::SystemAllocator, 0);
Type* m_value{ nullptr };
~PointerWrapper()
{
// Using free because not all types can safely use delete. Since this just to clear the memory to satisfy the memory
// leak test, this is fine.
azfree(m_value);
}
};
void SetUp() override
{
using namespace AZ::JsonSerializationResult;
@@ -165,6 +200,7 @@ namespace JsonSerializationTests
descriptor->ConfigureFeatures(this->m_features);
descriptor->Reflect(this->m_serializeContext);
descriptor->Reflect(this->m_jsonRegistrationContext);
this->m_serializeContext->template Class<PointerWrapper>()->Field("Value", &PointerWrapper::m_value);
this->m_deserializationSettings->m_reporting = &Internal::VerifyCallback;
this->m_serializationSettings->m_reporting = &Internal::VerifyCallback;
@@ -185,6 +221,7 @@ namespace JsonSerializationTests
this->m_jsonRegistrationContext->DisableRemoveReflection();
this->m_serializeContext->EnableRemoveReflection();
this->m_serializeContext->template Class<PointerWrapper>()->Field("Value", &PointerWrapper::m_value);
descriptor->Reflect(this->m_serializeContext);
this->m_serializeContext->DisableRemoveReflection();
@@ -487,6 +524,41 @@ namespace JsonSerializationTests
}
}
TYPED_TEST_P(JsonSerializerConformityTests, Load_DeserializeArrayWithDefaultValue_SucceedsAndReportPartialDefaults)
{
using namespace AZ::JsonSerializationResult;
if (this->m_features.SupportsJsonType(rapidjson::kArrayType))
{
this->m_jsonDocument->Parse(this->m_description.GetJsonFor_Load_DeserializeArrayWithDefaultValue().data());
ASSERT_FALSE(this->m_jsonDocument->HasParseError());
auto serializer = this->m_description.CreateSerializer();
auto instance = this->m_description.CreateDefaultInstance();
this->m_jsonDeserializationContext->PushPath(DefaultPath);
ResultCode result =
serializer->Load(instance.get(), azrtti_typeid(*instance), *this->m_jsonDocument, *this->m_jsonDeserializationContext);
if (this->m_features.m_fixedSizeArray)
{
EXPECT_EQ(Outcomes::Unsupported, result.GetOutcome());
EXPECT_EQ(Processing::Altered, result.GetProcessing());
}
else
{
EXPECT_EQ(Outcomes::PartialDefaults, result.GetOutcome());
EXPECT_EQ(Processing::Completed, result.GetProcessing());
auto compare = this->m_description.CreateSingleArrayDefaultInstance();
ASSERT_NE(nullptr, compare)
<< "Conformity tests for variably sized arrays require an implementation of CreateSingleArrayDefaultInstance";
EXPECT_TRUE(this->m_description.AreEqual(*compare, *instance));
}
}
}
TYPED_TEST_P(JsonSerializerConformityTests, Load_InterruptClearingTarget_ContainerIsNotCleared)
{
using namespace AZ::JsonSerializationResult;
@@ -548,7 +620,6 @@ namespace JsonSerializationTests
this->m_jsonDocument->Parse(json.data());
ASSERT_FALSE(this->m_jsonDocument->HasParseError());
auto serializer = this->m_description.CreateSerializer();
auto instance = this->m_description.CreateDefaultConstructedInstance();
auto compare = this->m_description.CreateFullySetInstance();
@@ -622,6 +693,94 @@ namespace JsonSerializationTests
}
}
TYPED_TEST_P(JsonSerializerConformityTests, Load_DeserializeFullInstanceOnTopOfPartialDefaulted_SucceedsAndObjectMatchesParialInstance)
{
using namespace AZ::JsonSerializationResult;
if (this->m_features.m_supportsPartialInitialization)
{
AZStd::string_view json = this->m_description.GetJsonFor_Load_DeserializeFullInstanceOnTopOfPartialDefaulted();
// If tests for partial initialization are enabled than json for the partial initialization is needed.
ASSERT_FALSE(json.empty());
this->m_jsonDocument->Parse(json.data());
ASSERT_FALSE(this->m_jsonDocument->HasParseError());
auto serializer = this->m_description.CreateSerializer();
auto instance = this->m_description.CreatePartialDefaultInstance();
auto compare = this->m_description.CreateFullySetInstance();
ASSERT_NE(nullptr, compare);
// Clear containers which should effectively turn them into default containers.
this->m_deserializationSettings->m_clearContainers = true;
this->ResetJsonContexts();
this->m_jsonDeserializationContext->PushPath(DefaultPath);
ResultCode result =
serializer->Load(instance.get(), azrtti_typeid(*instance), *this->m_jsonDocument, *this->m_jsonDeserializationContext);
EXPECT_EQ(Outcomes::Success, result.GetOutcome());
EXPECT_EQ(Processing::Completed, result.GetProcessing());
EXPECT_TRUE(this->m_description.AreEqual(*instance, *compare));
}
}
TYPED_TEST_P(JsonSerializerConformityTests, Load_DefaultToPointer_SucceedsAndValueIsInitialized)
{
using namespace AZ::JsonSerializationResult;
if (this->m_features.m_enableNewInstanceTests && this->m_features.m_mandatoryFields.empty())
{
AZ::SerializeContext* serializeContext = this->m_jsonDeserializationContext->GetSerializeContext();
const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(azrtti_typeid<typename TypeParam::Type>());
ASSERT_NE(nullptr, classData);
// Skip this test if the target type doesn't have a factor to create a new instance with or if the factor explicit
// prohibits construction.
if (classData->m_factory && classData->m_factory != AZ::Internal::NullFactory::GetInstance())
{
typename JsonSerializerConformityTests<TypeParam>::PointerWrapper instance;
auto compare = this->m_description.CreateDefaultInstance();
this->m_jsonDocument->Parse(R"({ "Value": {}})");
ASSERT_FALSE(this->m_jsonDocument->HasParseError());
AZ::JsonDeserializerSettings settings;
settings.m_serializeContext = serializeContext;
settings.m_registrationContext = this->m_jsonDeserializationContext->GetRegistrationContext();
ResultCode result = AZ::JsonSerialization::Load(instance, *this->m_jsonDocument, settings);
EXPECT_EQ(Outcomes::DefaultsUsed, result.GetOutcome());
EXPECT_EQ(Processing::Completed, result.GetProcessing());
ASSERT_NE(nullptr, instance.m_value);
EXPECT_TRUE(this->m_description.AreEqual(*instance.m_value, *compare));
}
}
}
TYPED_TEST_P(JsonSerializerConformityTests, Load_InitializeNewInstance_SucceedsAndValueIsInitialized)
{
using namespace AZ;
using namespace AZ::JsonSerializationResult;
if (this->m_features.m_enableNewInstanceTests && this->m_features.m_mandatoryFields.empty())
{
auto serializer = this->m_description.CreateSerializer();
if ((serializer->GetOperationsFlags() & BaseJsonSerializer::OperationFlags::InitializeNewInstance) ==
BaseJsonSerializer::OperationFlags::InitializeNewInstance)
{
typename TypeParam::Type instance;
auto compare = this->m_description.CreateDefaultInstance();
this->m_jsonDocument->SetObject();
ResultCode result =
serializer->Load(&instance, azrtti_typeid(instance), *this->m_jsonDocument, *this->m_jsonDeserializationContext);
EXPECT_EQ(Outcomes::DefaultsUsed, result.GetOutcome());
EXPECT_EQ(Processing::Completed, result.GetProcessing());
EXPECT_TRUE(this->m_description.AreEqual(instance, *compare));
}
}
}
TYPED_TEST_P(JsonSerializerConformityTests, Load_HaltedThroughCallback_LoadFailsAndHaltReported)
{
using namespace AZ::JsonSerializationResult;
@@ -909,6 +1068,28 @@ namespace JsonSerializationTests
}
}
TYPED_TEST_P(JsonSerializerConformityTests, Store_SerializeArrayWithSingleDefaultValue_StoredSuccessfullyAndJsonMatches)
{
using namespace AZ::JsonSerializationResult;
if (this->m_features.SupportsJsonType(rapidjson::kArrayType) && !this->m_features.m_fixedSizeArray)
{
this->m_jsonSerializationContext->PushPath(DefaultPath);
auto serializer = this->m_description.CreateSerializer();
auto instance = this->m_description.CreateSingleArrayDefaultInstance();
ASSERT_NE(nullptr, instance)
<< "Conformity tests for variably sized arrays require an implementation of CreateSingleArrayDefaultInstance";
ResultCode result = serializer->Store(
*this->m_jsonDocument, instance.get(), instance.get(), azrtti_typeid(*instance), *this->m_jsonSerializationContext);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
EXPECT_EQ(Outcomes::PartialDefaults, result.GetOutcome());
this->Expect_DocStrEq(this->m_description.GetJsonFor_Store_SerializeArrayWithSingleDefaultValue());
}
}
TYPED_TEST_P(JsonSerializerConformityTests, Store_HaltedThroughCallback_StoreFailsAndHaltReported)
{
using namespace AZ::JsonSerializationResult;
@@ -1017,7 +1198,29 @@ namespace JsonSerializationTests
}
}
TYPED_TEST_P(JsonSerializerConformityTests, GetOperationsFlags_ManualDefaultSetIfNeeded_ManualDefaultOperationSetIfMandatoryFieldsAreDeclared)
TYPED_TEST_P(JsonSerializerConformityTests, GetOperationFlags_RequiresExplicitInit_ObjectsThatDoNotConstructHaveExplicitInitOption)
{
using namespace AZ;
using namespace AZ::JsonSerializationResult;
if (this->m_features.m_enableInitializationTest)
{
auto instance = this->m_description.CreateDefaultInstance();
typename TypeParam::Type compare = typename TypeParam::Type{};
if (!this->m_description.AreEqual(*instance, compare))
{
auto serializer = this->m_description.CreateSerializer();
BaseJsonSerializer::OperationFlags flags = serializer->GetOperationsFlags();
bool hasManualDefaultSet =
(flags & BaseJsonSerializer::OperationFlags::ManualDefault) == BaseJsonSerializer::OperationFlags::ManualDefault ||
(flags & BaseJsonSerializer::OperationFlags::InitializeNewInstance) ==
BaseJsonSerializer::OperationFlags::InitializeNewInstance;
EXPECT_TRUE(hasManualDefaultSet);
}
}
}
TYPED_TEST_P(JsonSerializerConformityTests, GetOperationFlags_ManualDefaultSetIfNeeded_ManualDefaultOperationSetIfMandatoryFieldsAreDeclared)
{
if (this->m_features.SupportsJsonType(rapidjson::kObjectType))
{
@@ -1048,10 +1251,14 @@ namespace JsonSerializationTests
Load_DeserializeEmptyArray_SucceedsAndObjectMatchesDefaults,
Load_DeserializeEmptyArrayWithClearEnabled_SucceedsAndObjectMatchesDefaults,
Load_DeserializeEmptyArrayWithClearedTarget_SucceedsAndObjectMatchesDefaults,
Load_DeserializeArrayWithDefaultValue_SucceedsAndReportPartialDefaults,
Load_InterruptClearingTarget_ContainerIsNotCleared,
Load_DeserializeFullySetInstance_SucceedsAndObjectMatchesFullySetInstance,
Load_DeserializeFullySetInstanceThroughMainLoad_SucceedsAndObjectMatchesFullySetInstance,
Load_DeserializePartialInstance_SucceedsAndObjectMatchesParialInstance,
Load_DeserializeFullInstanceOnTopOfPartialDefaulted_SucceedsAndObjectMatchesParialInstance,
Load_DefaultToPointer_SucceedsAndValueIsInitialized,
Load_InitializeNewInstance_SucceedsAndValueIsInitialized,
Load_DeserializeWithMissingMandatoryField_LoadFailedAndUnsupportedReported,
Load_InsertAdditionalData_SucceedsAndObjectMatchesFullySetInstance,
Load_HaltedThroughCallback_LoadFailsAndHaltReported,
@@ -1066,13 +1273,15 @@ namespace JsonSerializationTests
Store_SerializeWithoutDefaultAndDefaultsKept_StoredSuccessfullyAndJsonMatches,
Store_SerializePartialInstance_StoredSuccessfullyAndJsonMatches,
Store_SerializeEmptyArray_StoredSuccessfullyAndJsonMatches,
Store_SerializeArrayWithSingleDefaultValue_StoredSuccessfullyAndJsonMatches,
Store_HaltedThroughCallback_StoreFailsAndHaltReported,
StoreLoad_RoundTripWithPartialDefault_IdenticalInstances,
StoreLoad_RoundTripWithFullSet_IdenticalInstances,
StoreLoad_RoundTripWithDefaultsKept_IdenticalInstances,
GetOperationsFlags_ManualDefaultSetIfNeeded_ManualDefaultOperationSetIfMandatoryFieldsAreDeclared);
GetOperationFlags_RequiresExplicitInit_ObjectsThatDoNotConstructHaveExplicitInitOption,
GetOperationFlags_ManualDefaultSetIfNeeded_ManualDefaultOperationSetIfMandatoryFieldsAreDeclared);
} // namespace JsonSerializationTests
namespace AZ
@@ -35,6 +35,11 @@ namespace JsonSerializationTests
return AZStd::make_shared<Map>();
}
AZStd::string_view GetJsonForSingleArrayDefaultInstance() override
{
return R"({ "{}": {} })";
}
void ConfigureFeatures(JsonSerializerConformityTestDescriptorFeatures& features) override
{
features.EnableJsonType(rapidjson::kArrayType);
@@ -61,6 +66,13 @@ namespace JsonSerializationTests
public:
using Map = T<int, double>;
AZStd::shared_ptr<Map> CreateSingleArrayDefaultInstance() override
{
auto instance = AZStd::make_shared<Map>();
instance->emplace(AZStd::make_pair(0, 0.0));
return instance;
}
AZStd::shared_ptr<Map> CreateFullySetInstance() override
{
auto instance = AZStd::make_shared<Map>();
@@ -100,6 +112,13 @@ namespace JsonSerializationTests
public:
using Map = T<AZStd::string, double>;
AZStd::shared_ptr<Map> CreateSingleArrayDefaultInstance() override
{
auto instance = AZStd::make_shared<Map>();
instance->emplace(AZStd::make_pair(AZStd::string(), 0.0));
return instance;
}
AZStd::shared_ptr<Map> CreateFullySetInstance() override
{
auto instance = AZStd::make_shared<Map>();
@@ -163,6 +182,14 @@ namespace JsonSerializationTests
return instance;
}
AZStd::shared_ptr<Map> CreateSingleArrayDefaultInstance() override
{
auto instance = AZStd::shared_ptr<Map>(new Map{}, &Delete);
instance->emplace(AZStd::make_pair(aznew SimpleClass(), aznew SimpleClass()));
return instance;
}
AZStd::string_view GetJsonForPartialDefaultInstance() override
{
if constexpr (IsMultiMap)
@@ -237,13 +264,23 @@ namespace JsonSerializationTests
return false;
}
auto compare = [](typename Map::const_reference lhs, typename Map::const_reference rhs) -> bool
// Naive compare to avoid having to split up the test because comparing for ordered and unordered maps would need to be
// different.
for (auto&& [key, value] : lhs)
{
return
lhs.first->Equals(*rhs.first, true) &&
lhs.second->Equals(*rhs.second, true);
};
return AZStd::equal(lhs.begin(), lhs.end(), rhs.begin(), compare);
for (auto&& [keyCompare, valueCompare] : rhs)
{
if (key->Equals(*keyCompare, true))
{
if (!value->Equals(*valueCompare, true))
{
return false;
}
break;
}
}
}
return true;
}
};
@@ -393,6 +430,29 @@ namespace JsonSerializationTests
{
using namespace AZ::JsonSerializationResult;
m_jsonDocument->Parse(R"(
{
"{}": {}
})");
ASSERT_FALSE(m_jsonDocument->HasParseError());
TestStringMap values;
ResultCode result = m_unorderedMapSerializer.Load(&values, azrtti_typeid(&values), *m_jsonDocument, *m_jsonDeserializationContext);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
EXPECT_EQ(Outcomes::PartialDefaults, result.GetOutcome());
EXPECT_EQ(1, values.size());
auto defaultKey = values.find(TestString());
EXPECT_NE(values.end(), defaultKey);
EXPECT_STRCASEEQ(TestString().m_value.c_str(), defaultKey->second.m_value.c_str());
}
TEST_F(JsonMapSerializerTests, Load_DefaultForStringKeyAndAdditionalValue_LoadedBackWithDefaults)
{
using namespace AZ::JsonSerializationResult;
m_jsonDocument->Parse(R"(
{
"{}": {},
@@ -563,13 +623,13 @@ namespace JsonSerializationTests
EXPECT_EQ(Outcomes::Catastrophic, result.GetOutcome());
}
TEST_F(JsonMapSerializerTests, Load_DefaultValueInMultiMap_DefaultUsed)
TEST_F(JsonMapSerializerTests, Load_DefaultObjectInMultiMap_DefaultUsed)
{
using namespace AZ::JsonSerializationResult;
m_jsonDocument->Parse(R"(
{
"Hello": {}
"World": {}
})");
ASSERT_FALSE(m_jsonDocument->HasParseError());
@@ -581,17 +641,40 @@ namespace JsonSerializationTests
EXPECT_EQ(Outcomes::PartialDefaults, result.GetOutcome());
ASSERT_FALSE(values.empty());
EXPECT_STREQ("Hello", values.begin()->first.m_value.c_str());
EXPECT_STREQ("World", values.begin()->first.m_value.c_str());
EXPECT_STREQ(TestString().m_value.c_str(), values.begin()->second.m_value.c_str());
}
TEST_F(JsonMapSerializerTests, Load_DefaultArrayValueInMultiMap_DefaultUsed)
TEST_F(JsonMapSerializerTests, Load_FullDefaultObjectInMultiMap_DefaultUsed)
{
using namespace AZ::JsonSerializationResult;
m_jsonDocument->Parse(R"(
{
"Hello": [{}]
"{}": {}
})");
ASSERT_FALSE(m_jsonDocument->HasParseError());
TestStringMultiMap values;
ResultCode result =
m_unorderedMultiMapSerializer.Load(&values, azrtti_typeid(&values), *m_jsonDocument, *m_jsonDeserializationContext);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
EXPECT_EQ(Outcomes::PartialDefaults, result.GetOutcome());
ASSERT_FALSE(values.empty());
EXPECT_STREQ("Hello", values.begin()->first.m_value.c_str());
EXPECT_STREQ("Hello", values.begin()->second.m_value.c_str());
EXPECT_STREQ(TestString().m_value.c_str(), values.begin()->second.m_value.c_str());
}
TEST_F(JsonMapSerializerTests, Load_DefaultObjectValueInMultiMap_DefaultUsed)
{
using namespace AZ::JsonSerializationResult;
m_jsonDocument->Parse(R"(
{
"World": [{}]
})");
ASSERT_FALSE(m_jsonDocument->HasParseError());
@@ -603,7 +686,8 @@ namespace JsonSerializationTests
EXPECT_EQ(Outcomes::PartialDefaults, result.GetOutcome());
ASSERT_FALSE(values.empty());
EXPECT_STREQ("Hello", values.begin()->first.m_value.c_str());
EXPECT_STREQ("World", values.begin()->first.m_value.c_str());
EXPECT_STREQ("Hello", values.begin()->second.m_value.c_str());
EXPECT_STREQ(TestString().m_value.c_str(), values.begin()->second.m_value.c_str());
}
@@ -636,7 +720,7 @@ namespace JsonSerializationTests
azrtti_typeid(&values), *m_jsonSerializationContext);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
EXPECT_EQ(Outcomes::DefaultsUsed, result.GetOutcome());
EXPECT_EQ(Outcomes::PartialDefaults, result.GetOutcome());
Expect_DocStrEq(R"(
{
"{}": {}
@@ -654,7 +738,25 @@ namespace JsonSerializationTests
azrtti_typeid(&values), *m_jsonSerializationContext);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
EXPECT_EQ(Outcomes::DefaultsUsed, result.GetOutcome());
EXPECT_EQ(Outcomes::PartialDefaults, result.GetOutcome());
Expect_DocStrEq(R"(
{
"{}": {}
})");
}
TEST_F(JsonMapSerializerTests, Store_SingleAllDefaulValue_InitializedWithDefaults)
{
using namespace AZ::JsonSerializationResult;
SimpleClassMap values;
values.emplace(SimpleClass(), SimpleClass());
ResultCode result =
m_unorderedMapSerializer.Store(*m_jsonDocument, &values, nullptr, azrtti_typeid(&values), *m_jsonSerializationContext);
EXPECT_EQ(Processing::Completed, result.GetProcessing());
EXPECT_EQ(Outcomes::PartialDefaults, result.GetOutcome());
Expect_DocStrEq(R"(
{
"{}": {}
@@ -48,12 +48,12 @@ namespace JsonSerializationTests
AZStd::shared_ptr<Pair> CreateDefaultInstance() override
{
return AZStd::make_shared<Pair>(142, 242.0);
return AZStd::make_shared<Pair>(0, 0.0);
}
AZStd::shared_ptr<Pair> CreatePartialDefaultInstance() override
{
return AZStd::make_shared<Pair>(142, 288.0);
return AZStd::make_shared<Pair>(0, 288.0);
}
AZStd::shared_ptr<Pair> CreateFullySetInstance() override
@@ -102,12 +102,12 @@ namespace JsonSerializationTests
AZStd::shared_ptr<Tuple> CreateDefaultInstance() override
{
return AZStd::make_shared<Tuple>(142, 242.0, 342.0f);
return AZStd::make_shared<Tuple>(0, 0.0, 0.0f);
}
AZStd::shared_ptr<Tuple> CreatePartialDefaultInstance() override
{
return AZStd::make_shared<Tuple>(142, 288.0, 342.0f);
return AZStd::make_shared<Tuple>(0, 288.0, 0.0f);
}
AZStd::shared_ptr<Tuple> CreateFullySetInstance() override
@@ -345,6 +345,7 @@ namespace JsonSerializationTests
{
TupleSerializerTestsInternal::ConfigureFeatures(features);
features.m_supportsPartialInitialization = true;
features.m_enableNewInstanceTests = false;
}
void Reflect(AZStd::unique_ptr<AZ::SerializeContext>& context) override
@@ -447,14 +448,14 @@ namespace JsonSerializationTests
{
return AZStd::make_shared<Tuple>(
AZStd::vector<int>(),
AZStd::make_pair(442, ""));
AZStd::make_pair(0, ""));
}
AZStd::shared_ptr<Tuple> CreatePartialDefaultInstance() override
{
return AZStd::make_shared<Tuple>(
AZStd::vector<int>(),
AZStd::make_pair(442, "hello"));
AZStd::make_pair(0, "hello"));
}
AZStd::shared_ptr<Tuple> CreateFullySetInstance() override
@@ -36,6 +36,11 @@ namespace JsonSerializationTests
return AZStd::make_shared<Set>();
}
AZStd::shared_ptr<Set> CreateSingleArrayDefaultInstance() override
{
return AZStd::make_shared<Set>(Set{ 0 });
}
AZStd::shared_ptr<Set> CreateFullySetInstance() override
{
return AZStd::make_shared<Set>(Set{42, -88, 342});
@@ -80,6 +85,11 @@ namespace JsonSerializationTests
return AZStd::make_shared<MultiSet>();
}
AZStd::shared_ptr<MultiSet> CreateSingleArrayDefaultInstance() override
{
return AZStd::make_shared<MultiSet>(MultiSet{ 0 });
}
AZStd::shared_ptr<MultiSet> CreateFullySetInstance() override
{
return AZStd::make_shared<MultiSet>(MultiSet{ 42, -88, 42, 342 });
@@ -48,7 +48,8 @@ namespace AzPhysics
using OnPresimulateEvent = AZ::Event<float>;
//! Event triggers at the end of the SystemInterface::Simulate call.
using OnPostsimulateEvent = AZ::Event<>;
//! Parameter is the total time that the physics system will run for during the Simulate call.
using OnPostsimulateEvent = AZ::Event<float>;
//! Event trigger when a Scene is added to the simulation.
//! When triggered will send the handle to the new Scene.
@@ -402,7 +402,7 @@ namespace Physics
->Attribute(AZ_CRC_CE("EditButton"), "")
->Attribute(AZ_CRC_CE("EditDescription"), "Open in Asset Editor")
->Attribute(AZ::Edit::Attributes::DefaultAsset, &MaterialSelection::GetMaterialLibraryId)
->DataElement(AZ::Edit::UIHandlers::Default, &MaterialSelection::m_materialIdsAssignedToSlots, "", "")
->DataElement(AZ::Edit::UIHandlers::Default, &MaterialSelection::m_materialIdsAssignedToSlots, "Slots", "")
->ElementAttribute(Attributes::MaterialLibraryAssetId, &MaterialSelection::GetMaterialLibraryId)
->Attribute(AZ::Edit::Attributes::IndexedChildNameLabelOverride, &MaterialSelection::GetMaterialSlotLabel)
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
@@ -43,7 +43,7 @@ namespace AzPhysics
const AZ::BehaviorAzEventDescription postsimulateEventDescription =
{
"Postsimulate event",
{} // Parameters
{"Tick time"} // Parameters
};
behaviorContext->Class<SystemInterface>("System Interface")
@@ -44,6 +44,11 @@ namespace AzNetworking
//! @class IConnection
//! @brief interface class for network connections.
//!
//! IConnection provides a pure-virtual interface for all network connection types. IConnections provide access to
//! a ConnectionMetrics object which provides a variety of metrics on the connection itself such as data rate, RTT and
//! packet statistics.
class IConnection
{
public:
@@ -22,6 +22,12 @@ namespace AzNetworking
{
//! @class IConnectionListener
//! @brief interface class for application layer dealing with connection level events.
//!
//! IConnectionListener defines an abstract interface that the user of AzNetworking is expected to implement to react and
//! handle all IConnection related events, including the handling of any received IPacket derived packets. The AzNetworking
//! user should derive a handler class from IConnectionListener, and provide an instance of that handler to any
//! INetworkInterface the user instantiates. The lifetime of the IConnectionListener must outlive the lifetime of the
//! INetworkInterface.
class IConnectionListener
{
public:
@@ -18,6 +18,11 @@ namespace AzNetworking
{
//! @class IConnectionSet
//! @brief interface class for managing a set of connections.
//!
//! IConnectionSet defines a simple interface for working with an abstract set of IConnections bound to an
//! INetworkInterface. Generally users of AzNetworking will not have reason to interact directly with the IConnectionSet,
//! as its interface is completely wrapped by INetworkInterface.
class IConnectionSet
{
public:
@@ -23,10 +23,10 @@ namespace AzNetworking
//! Collection of compression related error codes
enum class CompressorError
{
Ok, ///< No error, operation finished successfully
InsufficientBuffer, ///< Buffer size is insufficient for the operation to complete, increase the size and try again
CorruptData, ///< Malformed or hacked packet, potentially security issue
Uninitialized ///< Compressor or supplied buffers are uninitialized
Ok, //!< No error, operation finished successfully
InsufficientBuffer, //!< Buffer size is insufficient for the operation to complete, increase the size and try again
CorruptData, //!< Malformed or hacked packet, potentially security issue
Uninitialized //!< Compressor or supplied buffers are uninitialized
};
//! Unique identifier of a given compressor
@@ -34,6 +34,12 @@ namespace AzNetworking
//! @class ICompressor
//! @brief Packet data compressor interface.
//!
//! ICompressor is an abstract compression interface meant for user provided GEMs to implement (such as the [Multiplayer
//! Compression Gem](http://docs.o3de.org/docs/user-guide/gems/reference/multiplayer-compression)).
//! Compression is supported for both TCP and UDP connections. Instantiation of a compressor is controlled by the
//! `net_UdpCompressor` or `net_TcpCompressor` cvar for their respective protocols.
class ICompressor
{
public:
@@ -87,8 +93,16 @@ namespace AzNetworking
) = 0;
};
//! Abstract factory to instantiate compressors.
//! Used by the network interface to create a compressor
//! @class ICompressorFactory
//! @brief Abstract factory to instantiate compressors.
//!
//! ICompressorFactory is an abstract compression interface meant for user provided GEMs to implement. ICompressorFactory
//! implementations can be registered to classes implementing INetworking. Registered factories can then be used to create
//! ICompressor implementations on demand. The [Multiplayer Compression
//! Gem](http://docs.o3de.org/docs/user-guide/gems/reference/multiplayer-compression) is an example of an ICompressorFactory
//! for an LZ4 Compressor. In it, MultiplayerCompressionSystemComponent registers its ICompressorFactory with
//! NetworkingSystemComponent, which is an implementation of INetworking. Registered factories are keyed by their AZ Name
//! which is accessed through the factory's GetFactoryName method.
class ICompressorFactory
{
public:
@@ -22,7 +22,16 @@
namespace AzNetworking
{
//! @class INetworkInterface
//! @brief pure virtual network interface class to abstract client/server and tcp/udp concerns from application code.
//! @brief Network interface class to abstract client/server and protocol concerns from application code.
//!
//! INetworkInterface provides an abstract API capable of receiving and opening IConnection objects, sending IPacket objects with optional
//! reliability, and determining the delivery status of packets that have been sent unreliably (delivery of reliable packets
//! is guaranteed as long as the associated connection remains open). INetworkInterface must be provided an
//! IConnectionListener instance that outlives the INetworkInterface itself. The INetworkInterface also creates and manages
//! the IConnectionSet, which tracks all open connections bound to the interface. INetworkInterface also provides GetMetrics
//! functions which can be used to fetch a struct detailing a variety of metrics relating to send and receive rates for both
//! packets and bytes in addition to the effect of features on those rates (such as packet size reduction due to compression.)
class INetworkInterface
{
public:
@@ -23,6 +23,17 @@ namespace AzNetworking
//! @class INetworking
//! @brief The interface for creating and working with network interfaces.
//!
//! INetworking is an Az::Interface<T> that provides applications access to higher level networking abstractions.
//! AzNetworking::INetworking can be used to instantiate new INetworkInterfaces that can be configured to operate over
//! either TCP or UDP, enable or disable encryption, and be assigned a trust level.
//!
//! INetworking is also responsible for registering ICompressorFactory implementations. This allows a developer to have
//! access to multiple ICompressorFactory implementations by name. The [MultiplayerCompressor
//! Gem](http://docs.o3de.org/docs/user-guide/gems/reference/multiplayer-compression) is an example of this using the
//! [LZ4](https://wikipedia.org/wiki/LZ4_%28compression_algorithm%29) algorithm.
//!
class INetworking
{
public:
@@ -24,6 +24,15 @@ namespace AzNetworking
//! @class IPacket
//! @brief Base class for all packets.
//!
//! IPacket defines an abstract interface that all packets transmitted using AzNetworking must conform to. While there are
//! a number of core packets used internally by AzNetworking, it is fully possible for end-users to define their own custom
//! packets using this interface. PacketType should be distinct, and should be greater than
//! AzNetworking::CorePackets::MAX. The Serialize method allows the IPacket to be used by an
//! ISerializer to move data between hosts safely and efficiently.
//!
//! For more information on the packet format and best practices for extending the packet system, read
//! [Networking Packets](http://docs.o3de.org/docs/user-guide/networking/packets) on the O3DE documentation site.
class IPacket
{
public:
@@ -28,6 +28,19 @@ namespace AzNetworking
//! @class IPacketHeader
//! @brief A packet header that lets us deduce packet type for any incoming packet.
//!
//! IPacketHeader defines an abstract interface for a descriptor of all AzNetworking::IPacket sent through AzNetworking. The
//! PacketHeader is used to identify and describe the contents of a Packet so that transport logic can identify what
//! additional processing steps need to be taken (if any) and what type of Packet is being inspected.
//!
//! The PacketFlags portion of the header represents the first byte of the header. While it can be encrypted it is
//! otherwise not exposed to additional processing (such as an AzNetworking::ICompressor). PacketFlags are a bitfield use to provide up
//! front information about the state of the packet. Currently there is only one flag to indicate if the Packet is
//! compressed or not.
//!
//! The remainder of the header contains the PacketType and the PacketId. While the PacketFlags byte is exempt from most
//! additional forms of processing, the remainder of the header is not.
class IPacketHeader
{
public:

Some files were not shown because too many files have changed in this diff Show More