Merge pull request #1125 from aws-lumberyard-dev/carlitosan-beta-fixes

Demo support updates and fixes to be integrated into main
This commit is contained in:
carlitosan
2021-06-03 15:24:52 -07:00
committed by GitHub
21 changed files with 4416 additions and 5246 deletions
@@ -127,13 +127,14 @@ namespace AZ
*/
class EditContext
{
public:
/// @cond EXCLUDE_DOCS
class ClassBuilder;
class EnumBuilder;
using ClassInfo = ClassBuilder; ///< @deprecated Use EditContext::ClassBuilder
using EnumInfo = EnumBuilder; ///< @deprecated Use EditContext::EnumBuilder
/// @endcond
public:
AZ_CLASS_ALLOCATOR(EditContext, SystemAllocator, 0);
/**
@@ -186,6 +187,7 @@ namespace AZ
* look at the unit tests and example to see use cases.
*
*/
public:
class ClassBuilder
{
friend EditContext;
@@ -399,6 +401,7 @@ namespace AZ
EnumBuilder* Value(const char* name, E value);
};
private:
typedef AZStd::list<Edit::ClassData> ClassDataListType;
typedef AZStd::unordered_map<AZ::Uuid, Edit::ElementData> EnumDataMapType;
@@ -101,6 +101,9 @@ namespace AZ
class SerializeContext
: public ReflectContext
{
static const unsigned int VersionClassDeprecated = (unsigned int)-1;
public:
/// @cond EXCLUDE_DOCS
friend class EditContext;
class ClassBuilder;
@@ -108,9 +111,6 @@ namespace AZ
/// @endcond
class EnumBuilder;
static const unsigned int VersionClassDeprecated = (unsigned int)-1;
public:
class ClassData;
struct EnumerateInstanceCallContext;
struct ClassElement;
@@ -1131,6 +1131,7 @@ namespace AZ
* ->Version(3,&MyVersionConverter)
* ->Field("data",&MyStruct::m_data);
*/
public:
class ClassBuilder
{
friend class SerializeContext;
@@ -1330,7 +1331,8 @@ namespace AZ
AZStd::vector<AttributeSharedPair, AZStdFunctorAllocator>* m_currentAttributes = nullptr;
};
EditContext* m_editContext; ///< Pointer to optional edit context.
private:
EditContext* m_editContext; ///< Pointer to optional edit context.
UuidToClassMap m_uuidMap; ///< Map for all class in this serialize context
AZStd::unordered_multimap<AZ::Crc32, AZ::Uuid> m_classNameToUuid; /// Map all class names to their uuid
AZStd::unordered_multimap<Uuid, GenericClassInfo*> m_uuidGenericMap; ///< Uuid to ClassData map of reflected classes with GenericTypeInfo
@@ -91,7 +91,8 @@ AZ_POP_DISABLE_WARNING
#include <ScriptCanvas/Variable/VariableBus.h>
#include <ScriptCanvas/Libraries/UnitTesting/UnitTestingLibrary.h>
////
AZ_CVAR(bool, g_disableDeprecatedNodeUpdates, false, {}, AZ::ConsoleFunctorFlags::Null,
"Disables automatic update attempts of deprecated nodes, so that graphs that require and update can be viewed in their original form");
namespace EditorGraphCpp
{
@@ -3642,7 +3643,7 @@ namespace ScriptCanvasEditor
if (scriptCanvasNode)
{
if (scriptCanvasNode->IsDeprecated())
if (scriptCanvasNode->IsDeprecated() && !g_disableDeprecatedNodeUpdates)
{
ScriptCanvas::NodeConfiguration nodeConfig = scriptCanvasNode->GetReplacementNodeConfiguration();
if (nodeConfig.IsValid())
@@ -22,6 +22,11 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#pragma once
#include <AzCore/RTTI/TypeInfo.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <ScriptCanvas/Core/Nodeable.h>
#include <ScriptCanvas/Core/NodeableNode.h>
@@ -43,6 +48,11 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
{% endif %}
{% endif %}
{%- set attribute_Base = Class.attrib['Base'] %}
{% if not Class.attrib['Base'] is defined %}
{% set attribute_Base = "ScriptCanvas::Nodeable" %}
{% endif %}
{% if attribute_Namespace is defined %}
namespace {{attribute_Namespace}}
{
@@ -66,6 +76,9 @@ namespace {{attribute_Namespace}}
public: \
AZ_RTTI({{className}}, "{{nodeableClassName|createHashGuid}}"{% if Class.attrib['Base'] is defined %}, {{ Class.attrib['Base'] }}{% endif %}); \
static void Reflect(AZ::ReflectContext* reflection); \
static void ExtendReflectionSerialize([[maybe_unused]] AZ::SerializeContext::ClassBuilder* builder){% if Class.attrib['ExtendReflectionSerialize'] is defined %};{% else %}{}{% endif %} \
static void ExtendReflectionEdit([[maybe_unused]] AZ::EditContext::ClassBuilder* builder){% if Class.attrib['ExtendReflectionEdit'] is defined %};{% else %}{}{% endif %} \
static void ExtendReflectionBehavior([[maybe_unused]] AZ::BehaviorContext::ClassBuilder<{{className}}>* builder){% if Class.attrib['ExtendReflectionBehavior'] is defined %};{% else %}{}{% endif %} \
static const char* GetDescription() { return "{{ macros.GetAttributeAsString(Class.attrib, 'Description') }}"; } \
ScriptCanvas::NodePropertyInterface* GetPropertyInterface(AZ::Crc32 propertyId) override; \
bool IsActive() const override { return false; } \
@@ -83,13 +96,33 @@ public: \
AZ_COMPONENT({{nodeableNodeName}}, {% if Class.attrib['NodeableUuid'] is defined %}"{{Class.attrib['NodeableUuid']}}"{% else %}"{{nodeableNodeName|createHashGuid}}"{% endif %}, ScriptCanvas::Nodes::NodeableNode);
static void Reflect(AZ::ReflectContext* context);
static void ExtendReflectionSerialize([[maybe_unused]] AZ::SerializeContext::ClassBuilder* builder){% if Class.attrib['ExtendReflectionSerialize'] is defined %};{% else %}{}{% endif %}
static void ExtendReflectionEdit([[maybe_unused]] AZ::EditContext::ClassBuilder* builder){% if Class.attrib['ExtendReflectionEdit'] is defined %};{% else %}{}{% endif %}
static void ExtendReflectionBehavior([[maybe_unused]] AZ::BehaviorContext::ClassBuilder<{{nodeableNodeName}}>* builder){% if Class.attrib['ExtendReflectionBehavior'] is defined %};{% else %}{}{% endif %}
void ConfigureSlots() override;
{% if Class.attrib['ExtendConfigureSlots'] is defined %}
void ExtendConfigureSlots([[maybe_unused]] SlotExecution::Ins& ins, [[maybe_unused]] SlotExecution::Outs& latents);
{% else %}
/* no slot configuration extension, Use Class attribute 'ExtendConfigureSlots' to extend them */
{% endif %}
void ConfigureVisualExtensions() override;
size_t GenerateFingerprint() const override;
{% if Class.attrib['EntryPoint'] is defined and Class.attrib['EntryPoint'] == "true" %}
{% if Class.attrib['EntryPoint'] is defined and Class.attrib['EntryPoint'] == "true" %}
bool IsEntryPoint() const override { return true; }
{% endif %}
{% endif %}
{{nodeableNodeName}}();
{{Class.attrib['NodeDeclarations']}}
};
}
{% endif %}
@@ -100,6 +133,5 @@ public: \
{{ macros.ReportErrors() }}
{% endfor %}
{% endfor %}
{% endfor %}
@@ -19,15 +19,10 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/RTTI/TypeInfo.h>
#include <ScriptCanvas/Core/Contracts.h>
#include <ScriptCanvas/Core/NodeableNode.h>
#include <ScriptCanvas/Core/SlotExecutionMap.h>
#include <ScriptCanvas/Grammar/ParsingUtilities.h>
#include <ScriptCanvas/Utils/VersionConverters.h>
{% for xml in dataFiles %}
@@ -47,13 +42,18 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
{%- set attribute_Category = Class.attrib['Category'] %}
{%- set attribute_Uuid = Class.attrib['Uuid'] %}
{%- set attribute_Icon = Class.attrib['Icon'] %}
{%- set attribute_Base = Class.attrib['Base'] %}
{%- set attribute_GeneratePropertyFriend = Class.attrib['GeneratePropertyFriend'] %}
{%- set attribute_Version = Class.attrib['Version'] %}
{%- set attribute_VersionConverter = Class.attrib['VersionConverter'] %}
{%- set attribute_EventHandler = Class.attrib['EventHandler'] %}
{%- set attribute_Deprecated = Class.attrib['Deprecated'] %}
{%- set attribute_Base = Class.attrib['Base'] %}
{% if not Class.attrib['Base'] is defined %}
{% set attribute_Base = "ScriptCanvas::Nodeable" %}
{% endif %}
{% set attribute_Namespace = undefined %}
{%- if Class.attrib['Namespace'] is defined %}
{% if Class.attrib['Namespace'] != "None" %}
@@ -95,19 +95,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
{{CollectDisplayGroups('Output')}}
{{CollectDisplayGroups('Parameter')}}
{# FOR DEBUGGING / DIAGNOSTIC
// Standalone (No DisplayGroup) {{ global_standaloneTagMap }}
{% for key, value in global_standaloneTagMap.items() %}
// {{key}} : {{value}}
{% endfor %}
// DisplayGrouped {{ global_displayGroupMap }}
{% for key, value in global_displayGroupMap.items() %}
// {{key}} : {{value}}
{% endfor %}
#}
{# ----------------------------------------------------------------------------------------- #}
{% if attribute_Namespace is defined %}
@@ -115,33 +102,6 @@ namespace {{attribute_Namespace}}
{
{% endif %}
{# Standard "In" function }
{{nodemacro.FunctionSignature(attribute_QualifiedName, Class)}}
{
{%- for parameter in Class.findall('Parameter') -%}
{% if parameter.attrib['Input'] is defined and parameter.attrib['Input'] == "True" %}
// this->{{parameter.attrib['Name']}} = arg{{loop.index0}};
{%- endif -%}
{% endfor %}
{% set returnNames = [] %}
{% set returnTypes = [] %}
{%- for return in Class.findall('Parameter') -%}
{%- if return.attrib['Output'] is defined and return.attrib['Output'] == "True" -%}
{% if returnTypes.append(return.attrib['Type']) %}{% endif %}
{% if returnNames.append("this->" + return.attrib['Name']) %}{% endif %}
{%- endif -%}
{%- endfor -%}
{% if returnNames|length() == 1 %}
return {{returnNames[0]}};
{% elif returnNames|length() > 1 %}
return AZStd::tuple<{{returnTypes|join(", ")}}>({{returnNames|join(", ")}});
{% endif %}
}
#}
{%- set nodeableNodeName = attribute_Name + 'Node' %}
{% set list_outputs = [] %}
{% for output in Class.iter('Output') %}
@@ -156,43 +116,81 @@ return {{returnNames[0]}};
{% for item in Class.iter('Output') %}
{% if item.attrib['DisplayGroup'] is defined %}{% set displayGroup = item.attrib['DisplayGroup'] %}{% endif %}
{% endfor %}
{% set branches = [] %}
{% for method in Class.findall('Input') %}
{% for branch in method.findall('Branch') %}
{% if branches.append(branch) %}{% endif %}
{% endfor %}
{% endfor %}
{# ExecutionOuts #}
// ExecutionOuts begin
{{ nodemacro.ExecutionOutDefinitions(Class, attribute_QualifiedName)}}
{% if not Class.attrib['ExtendConfigureSlots'] is defined %} size_t {{attribute_QualifiedName}}::GetRequiredOutCount() const { return {{Class.findall('Output')|length + branches|length}}; }{% endif %}
// ExecutionOuts end
{# Reflect #}
{% if Class.attrib['ExtendReflectionSerialize'] is defined %}
{% set ExtendReflectionSerialize = "defined" %}
{% set preSerialize = "serializeBuilder" %}
{% set postSerialize = ";" %}
{% else %}
{% set preSerialize = "" %}
{% set postSerialize = "" %}
{% endif %}
{% if Class.attrib['ExtendReflectionEdit'] is defined %}
{% set ExtendReflectionEdit = "defined" %}
{% set preEdit = "editorBuilder" %}
{% set postEdit = ";" %}
{% else %}
{% set preEdit = "" %}
{% set postEdit = "" %}
{% endif %}
{% if Class.attrib['ExtendReflectionBehavior'] is defined %}
{% set ExtendReflectionBehavior = "defined" %}
{% set preBehavior = "behaviorBuilder" %}
{% set postBehavior = ";" %}
{% else %}
{% set preBehavior = "" %}
{% set postBehavior = "" %}
{% endif %}
void {{attribute_QualifiedName}}::Reflect(AZ::ReflectContext* context)
{
using namespace ScriptCanvas;
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<{{ attribute_Name }}{% if attribute_Base is defined %}, {{ attribute_Base }}{% endif %}>()
{% if ExtendReflectionSerialize is defined %} auto {{preSerialize}} = {% else %} {% endif %}serializeContext->Class<{{ attribute_Name }}{% if attribute_Base is defined %}, {{ attribute_Base }}{% endif %}>(){{postSerialize}}
{% if attribute_EventHandler is defined %}
->EventHandler<{{ attribute_EventHandler }}>()
{{preSerialize}}->EventHandler<{{ attribute_EventHandler }}>(){{postSerialize}}
{% endif %}
{# Serialized Properties #}
{% for Property in Class.iter('Property') %}
{% set property_Name = Property.attrib['Name'] %}
->Field("{{ property_Name }}", &{{ attribute_Name }}::{{ property_Name | replace(' ','') }})
{{preSerialize}}->Field("{{ property_Name }}", &{{ attribute_Name }}::{{ property_Name | replace(' ','') }}){{postSerialize}}
{% endfor %}
;
{% if ExtendReflectionSerialize is defined %}
ExtendReflectionSerialize(&{{preSerialize}});
{% endif %}
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<{{ attribute_QualifiedName }}>("{{ attribute_PreferredClassName }}", "{{ attribute_Description }}")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
{% if ExtendReflectionEdit is defined %} auto {{preEdit}} = {% else %} {% endif %}editContext->Class<{{ attribute_QualifiedName }}>("{{ attribute_PreferredClassName }}", "{{ attribute_Description }}"){{postEdit}}
{{preEdit}}->ClassElement(AZ::Edit::ClassElements::EditorData, ""){{postEdit}}
{{preEdit}}->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly){{postEdit}}
{% if attribute_Category is defined %}
->Attribute(AZ::Edit::Attributes::Category, "{{ attribute_Category }}")
{{preEdit}}->Attribute(AZ::Edit::Attributes::Category, "{{ attribute_Category }}"){{postEdit}}
{%- endif %}
{% if attribute_Icon is defined %}
->Attribute(AZ::Edit::Attributes::Icon, "{{ attribute_Icon }}")
{{preEdit}}->Attribute(AZ::Edit::Attributes::Icon, "{{ attribute_Icon }}"){{postEdit}}
{%- endif %}
{% if attribute_Deprecated is defined %}
->Attribute(AZ::Edit::Attributes::Deprecated, "{{ attribute_Deprecated }}")
{{preEdit}}->Attribute(AZ::Edit::Attributes::Deprecated, "{{ attribute_Deprecated }}"){{postEdit}}
{%- endif %}
{% set uihandler = 'AZ::Edit::UIHandlers::Default' %}
{% for item in Class.iter('Property') %}
@@ -203,29 +201,34 @@ void {{attribute_QualifiedName}}::Reflect(AZ::ReflectContext* context)
{% if item.attrib['Description'] is defined %}
{% set description = item.attrib['Description'] %}
{% endif %}
// {{ item.attrib['Name'] }}
->DataElement({{ uihandler }}, &{{ attribute_Name }}::{{ item.attrib['Name'] }}, "{{ item.attrib['Name'] }}", "{{ description }}")
// {{ item.attrib['Name'] }}
{{preEdit}}->DataElement({{ uihandler }}, &{{ attribute_Name }}::{{ item.attrib['Name'] }}, "{{ item.attrib['Name'] }}", "{{ description }}"){{postEdit}}
{% for EditAttribute in item.iter('EditAttribute') %}
->Attribute({{ EditAttribute.attrib['Key'] }}, {{ EditAttribute.attrib['Value'] }})
{{preEdit}}->Attribute({{ EditAttribute.attrib['Key'] }}, {{ EditAttribute.attrib['Value'] }}){{postEdit}}
{% endfor %}
{% endfor %}
;
{% if ExtendReflectionEdit is defined %}
ExtendReflectionEdit(&{{preEdit}});
{% endif %}
}
}
// Behavior Context Reflection
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<{{ attribute_Name }}>("{{ attribute_Name }}")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::List)
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
{% if ExtendReflectionBehavior is defined %} auto {{preBehavior}} = {% else %} {% endif %}behaviorContext->Class<{{ attribute_Name }}>("{{ attribute_Name }}"){{postBehavior}}
{{preBehavior}}->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::List){{postBehavior}}
{{preBehavior}}->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common){{postBehavior}}
{% for inputMethod in Class.iter('Input') %}
{% set methodName = inputMethod.attrib['Name'] %}
// {{ inputMethod.attrib['Name'] }}
->Method(Grammar::ToIdentifier("{{ macros.SlotName(methodName) }}").c_str(), &{{ attribute_Name }}::{{ macros.CleanName(methodName) }})
{{preBehavior}}->Method(Grammar::ToIdentifier("{{ macros.SlotName(methodName) }}").c_str(), &{{ attribute_Name }}::{{ macros.CleanName(methodName) }}){{postBehavior}}
{% endfor %}
;
{% if ExtendReflectionBehavior is defined %}
ExtendReflectionBehavior(&{{preBehavior}});
{% endif %}
}
}
@@ -253,27 +256,32 @@ Nodes::{{ nodeableNodeName }}::{{ nodeableNodeName }}()
{# NodeableNode Reflection #}
void Nodes::{{ nodeableNodeName }}::Reflect(AZ::ReflectContext* context)
{
{{ attribute_QualifiedName }}::Reflect(context);
// Serialization Context Reflection
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<{{ nodeableNodeName }}, NodeableNode>()
{%if ExtendReflectionSerialize is defined%}auto {{preSerialize}} = {%endif%}serializeContext->Class<{{ nodeableNodeName }}, NodeableNode>(){{postSerialize}}
{% if attribute_Version is defined %}
->Version({{ attribute_Version }}{% if attribute_VersionConverter is defined %}, &{{ attribute_VersionConverter }}{% endif %})
{{preSerialize}}->Version({{ attribute_Version }}{% if attribute_VersionConverter is defined %}, &{{ attribute_VersionConverter }}{% endif %}){{postSerialize}}
{% else %}
->Version(0)
{{preSerialize}}->Version(0){{postSerialize}}
{% endif %}
;
{% if ExtendReflectionSerialize is defined %}
ExtendReflectionSerialize(&{{preSerialize}});
{% endif %}
;
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<{{ nodeableNodeName }}>("{{ attribute_PreferredClassName }}", "{{ attribute_Description }}")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
;
{% if ExtendReflectionEdit is defined %}auto {{preEdit}} = {%endif%}editContext->Class<{{ nodeableNodeName }}>("{{ attribute_PreferredClassName }}", "{{ attribute_Description }}"){{postEdit}}
{{preEdit}}->ClassElement(AZ::Edit::ClassElements::EditorData, ""){{postEdit}}
{{preEdit}}->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly){{postEdit}}
{{preEdit}}->Attribute(AZ::Edit::Attributes::AutoExpand, true){{postEdit}}
;
{% if ExtendReflectionEdit is defined %}
ExtendReflectionEdit(&{{preEdit}});
{% endif %}
}
}
}
@@ -324,6 +332,7 @@ void Nodes::{{ nodeableNodeName }}::ConfigureVisualExtensions()
RegisterExtension(visualExtensions);
}
{% endfor %}
OnConfigureVisualExtensions();
}
{# ConfigureSlots #}
@@ -457,6 +466,9 @@ void Nodes::{{ nodeableNodeName }}::ConfigureSlots()
{% endfor %}
#}
{% if Class.attrib['ExtendConfigureSlots'] is defined %}
ExtendConfigureSlots(ins, outs);
{% endif %}
// Generate the execution map
m_slotExecutionMap = SlotExecution::Map(AZStd::move(ins), AZStd::move(outs));
@@ -349,7 +349,4 @@ void {{qualifiedName}}::Call{{CleanName(outName)}}({{ExecutionOutReturnDefinitio
{%- for executionOut in Class.findall('Output') -%}
{{ ExecutionOutDefinition(Class, qualifiedName, executionOut, loop.index0 + branches|length) }}
{%- endfor %}
size_t {{qualifiedName}}::GetRequiredOutCount() const { return {{Class.findall('Output')|length + branches|length}}; }
{% endmacro %}
{% endmacro %}
@@ -250,7 +250,6 @@ namespace ScriptCanvas
};
using ScriptCanvasSettingsRequestBus = AZ::EBus<ScriptCanvasSettingsRequests>;
}
namespace AZStd
@@ -1476,9 +1476,11 @@ namespace ScriptCanvas
const void* Datum::GetValueAddress() const
{
return m_type.GetType() != Data::eType::BehaviorContextObject
? AZStd::any_cast<void>(&m_storage)
: (*AZStd::any_cast<BehaviorContextObjectPtr>(&m_storage))->Get();
return !m_storage.empty()
? m_type.GetType() != Data::eType::BehaviorContextObject
? AZStd::any_cast<void>(&m_storage)
: (*AZStd::any_cast<BehaviorContextObjectPtr>(&m_storage))->Get()
: nullptr;
}
bool Datum::Initialize(const Data::Type& type, eOriginality originality, const void* source, const AZ::Uuid& sourceTypeID)
@@ -389,7 +389,7 @@ namespace ScriptCanvas
bool Datum::Empty() const
{
return GetValueAddress() == nullptr;
return m_storage.empty() || GetValueAddress() == nullptr;
}
template<typename t_Value>
@@ -3577,12 +3577,12 @@ namespace ScriptCanvas
}
if (targetSlotType == CombinedSlotType::DataOut
&& executionSlot.GetType() == CombinedSlotType::ExecutionIn
&& executionInCount > 1)
&& executionSlot.GetType() == CombinedSlotType::ExecutionIn
&& executionInCount > 1)
{
if (!executionChildSlot || executionChildSlot->GetType() != CombinedSlotType::ExecutionOut)
{
return AZ::Failure(AZStd::string("Data out by ExcutionIn must have child out slot"));
return AZ::Failure(AZStd::string("Data out by ExecutionIn must have child out slot"));
}
}
@@ -3626,6 +3626,11 @@ namespace ScriptCanvas
return {};
}
Grammar::MultipleFunctionCallFromSingleSlotInfo Node::GetMultipleFunctionCallFromSingleSlotInfo([[maybe_unused]] const Slot& slot) const
{
return {};
}
VariableId Node::GetVariableIdRead(const Slot*) const
{
return {};
@@ -715,6 +715,8 @@ namespace ScriptCanvas
virtual PropertyFields GetPropertyFields() const;
virtual Grammar::MultipleFunctionCallFromSingleSlotInfo GetMultipleFunctionCallFromSingleSlotInfo(const Slot& slot) const;
virtual VariableId GetVariableIdRead(const Slot*) const;
virtual VariableId GetVariableIdWritten(const Slot*) const;
@@ -73,6 +73,8 @@ namespace ScriptCanvas
void ConfigureSlots() override;
virtual void OnConfigureVisualExtensions() {}
AZ::Outcome<const AZ::BehaviorClass*, AZStd::string> GetBehaviorContextClass() const;
ConstSlotsOutcome GetBehaviorContextOutName(const Slot& inSlot) const;
@@ -46,6 +46,8 @@ namespace ScriptCanvas
const AZStd::unordered_map<AZStd::string, AZStd::pair<SlotId, SlotId>>& GetPropertyNameSlotMap() const;
AZ_INLINE AZ::Outcome<DependencyReport, void> GetDependencies() const override { return AZ::Success(DependencyReport{}); }
~PureData() override;
protected:
@@ -2249,6 +2249,10 @@ namespace ScriptCanvas
}
#endif
AddAllVariablesPreParse();
if (!IsErrorFree())
{
return;
}
for (auto& nodeEntity : m_source.m_graphData->m_nodes)
{
@@ -2270,6 +2274,16 @@ namespace ScriptCanvas
{
AddError(nullptr, ValidationConstPtr(aznew NullEntityInGraph()));
}
if (!IsErrorFree())
{
return;
}
}
if (!IsErrorFree())
{
return;
}
ParseAutoConnectedEBusHandlerVariables();
@@ -2638,7 +2652,8 @@ namespace ScriptCanvas
AZStd::vector<VariableId> inputVariableIds;
AZStd::unordered_map<VariableId, Grammar::VariableConstPtr> inputVariablesById;
for (auto variable : GetVariables())
auto& variables = GetVariables();
for (auto variable : variables)
{
auto constructionRequirement = ParseConstructionRequirement(variable);
@@ -2655,18 +2670,30 @@ namespace ScriptCanvas
case VariableConstructionRequirement::InputNodeable:
{
if (variable->m_datum.Empty())
{
AddError(nullptr, aznew Internal::ParseError(AZ::EntityId{}, "Empty nodeable datum in variable, probably due to a problem with azrtti declarations"));
break;
}
// I solemnly swear no harm shall come to the nodeable
const Nodeable* nodeableSource = reinterpret_cast<const Nodeable*>(variable->m_datum.GetAsDanger());
AZ_Assert(nodeableSource != nullptr, "the must be a raw nodeable held by this pointer");
AZ_Assert(azrtti_typeid(nodeableSource) != azrtti_typeid<Nodeable>(), "type problem with nodeable");
if (!nodeableSource)
{
AddError(nullptr, aznew Internal::ParseError(AZ::EntityId{}, "No raw nodeable held by variable"));
break;
}
nodeablesById.push_back({ variable->m_nodeableNodeId, const_cast<Nodeable*>(nodeableSource) });
}
break;
case VariableConstructionRequirement::InputVariable:
{
inputVariableIds.push_back(variable->m_sourceVariableId);
inputVariablesById.insert({ variable->m_sourceVariableId, variable });
auto variableID = variable->m_sourceVariableId.IsValid() ? variable->m_sourceVariableId : VariableId::MakeVariableId();
inputVariableIds.push_back(variableID);
inputVariablesById.insert({ variableID, variable });
// sort revealed a datum copy issue: type is not preserved, workaround below
// m_runtimeInputs.m_variables.emplace_back(variable->m_sourceVariableId, variable->m_datum);
}
@@ -4310,6 +4337,7 @@ namespace ScriptCanvas
{
if (auto variable = FindVariable(execution->GetNodeId()))
{
execution->MarkInputHasThisPointer();
execution->AddInput({ nullptr, variable, DebugDataSource::FromInternal() });
}
else
@@ -4327,6 +4355,7 @@ namespace ScriptCanvas
{
auto variable = AZStd::make_shared<Variable>();
variable->m_datum = Datum(eventHandling->m_handlerName);
execution->MarkInputHasThisPointer();
execution->AddInput({ nullptr, variable, DebugDataSource::FromInternal() });
}
else
@@ -4338,6 +4367,7 @@ namespace ScriptCanvas
{
if (auto variable = FindVariable(execution->GetNodeId()))
{
execution->MarkInputHasThisPointer();
execution->AddInput({ nullptr, variable, DebugDataSource::FromInternal() });
}
else
@@ -4367,12 +4397,159 @@ namespace ScriptCanvas
void AbstractCodeModel::ParseMultiExecutionPost(ExecutionTreePtr execution)
{
ParsePropertyExtractionsPost(execution);
ParseMultipleFunctionCallPost(execution);
}
void AbstractCodeModel::ParseMultiExecutionPre(ExecutionTreePtr execution)
{
ParsePropertyExtractionsPre(execution);
}
}
void AbstractCodeModel::ParseMultipleFunctionCallPost(ExecutionTreePtr execution)
{
auto& id = execution->GetId();
MultipleFunctionCallFromSingleSlotInfo info = id.m_node->GetMultipleFunctionCallFromSingleSlotInfo(*id.m_slot);
if (info.functionCalls.empty())
{
return;
}
auto parent = execution->ModParent();
if (!parent)
{
AddError(execution, aznew Internal::ParseError(id.m_node->GetEntityId(), "Null parent in MultipleFunctionCall"));
return;
}
size_t indexInParentCall = parent->FindChildIndex(execution);
if (indexInParentCall >= parent->GetChildrenCount())
{
AddError(execution, aznew Internal::ParseError(id.m_node->GetEntityId(), ParseErrors::MultipleFunctionCallFromSingleSlotNoChildren));
return;
}
ExecutionChild* executionChildInParent = &parent->ModChild(indexInParentCall);
const size_t executionInputCount = execution->GetInputCount();
const size_t thisInputOffset = execution->InputHasThisPointer() ? 1 : 0;
// the original index has ALL the input from the slots on the node
// create multiple calls with separate function call nodes, but ONLY take the inputs required
// as indicated by the function call info
AZStd::unordered_set<const Slot*> usedSlots;
bool variadicIsFound = false;
auto createChild = [&](auto parentCall, ExecutionChild* childInParent, auto& functionCallInfo)
{
auto child = CreateChild(parentCall, id.m_node, id.m_slot);
child->SetSymbol(Symbol::FunctionCall);
child->SetName(functionCallInfo.functionName);
child->SetNameLexicalScope(functionCallInfo.lexicalScope);
childInParent->m_execution = child;
return child;
};
auto addThisInput = [&](auto functionCall)
{
if (thisInputOffset != 0)
{
if (executionInputCount == 0)
{
AddError(execution, aznew Internal::ParseError(id.m_node->GetEntityId(), ParseErrors::MultipleFunctionCallFromSingleSlotNotEnoughInputForThis));
return;
}
const ExecutionInput& input = execution->GetInput(0);
usedSlots.insert(input.m_slot);
functionCall->AddInput(input);
}
};
auto addSlotInput = [&](auto functionCall, size_t inputIndex)
{
if (inputIndex >= executionInputCount)
{
AddError(execution, aznew Internal::ParseError(id.m_node->GetEntityId(), ParseErrors::MultipleFunctionCallFromSingleSlotNotEnoughInput));
return;
}
const ExecutionInput& input = execution->GetInput(inputIndex);
if (usedSlots.contains(input.m_slot))
{
AddError(execution, aznew Internal::ParseError(id.m_node->GetEntityId(), ParseErrors::MultipleFunctionCallFromSingleSlotNotEnoughInput));
return;
}
usedSlots.insert(input.m_slot);
if (input.m_value->m_source == execution)
{
input.m_value->m_source = functionCall;
}
functionCall->AddInput(input);
};
auto addCall = [&](auto& functionCallInfo, auto childInParent, size_t startingIndex, size_t sentinel, size_t variadicOffset = 0)
{
auto child = createChild(parent, childInParent, functionCallInfo);
addThisInput(child);
for (size_t index = startingIndex; index < sentinel; ++index)
{
const size_t inputIndex = index + thisInputOffset + variadicOffset;
addSlotInput(child, inputIndex);
}
child->AddChild({});
childInParent = &child->ModChild(0);
return AZStd::make_pair(childInParent, child);
};
// loop through each call...
for (auto& functionCallInfo : info.functionCalls)
{
// ...first add any pre-variadic calls, using the starting index and the number of args, since they could come in any order, not input slot order...
if (!functionCallInfo.isVariadic)
{
AZStd::pair<ExecutionChild*, ExecutionTreePtr> childInParentAndParent = addCall(functionCallInfo, executionChildInParent, functionCallInfo.startingIndex, functionCallInfo.startingIndex + functionCallInfo.numArguments);
executionChildInParent = childInParentAndParent.first;
parent = childInParentAndParent.second;
}
else
{
// ...then add only one variadic call if there is one...
if (variadicIsFound)
{
AddError(execution, aznew Internal::ParseError(id.m_node->GetEntityId(), ParseErrors::MultipleFunctionCallFromSingleSlotMultipleVariadic));
return;
}
variadicIsFound = true;
const size_t sentinel = executionInputCount == 0 ? 0 : executionInputCount - thisInputOffset;
// ... by looping through the remaining slots, striding by functionCallInfo.numArguments, making repeated calls to the function
for (size_t slotInputIndex = functionCallInfo.startingIndex; slotInputIndex < sentinel; slotInputIndex += functionCallInfo.numArguments)
{
AZStd::pair<ExecutionChild*, ExecutionTreePtr> childInParentAndParent = addCall(functionCallInfo, executionChildInParent, 0, functionCallInfo.numArguments, slotInputIndex);
executionChildInParent = childInParentAndParent.first;
parent = childInParentAndParent.second;
}
}
}
if (info.errorOnUnusedSlot && usedSlots.size() != executionInputCount)
{
AddError(execution, aznew Internal::ParseError(id.m_node->GetEntityId(), ParseErrors::MultipleFunctionCallFromSingleSlotUnused));
}
// parent now refers to the last child call created
parent->SwapChildren(execution);
execution->Clear();
}
void AbstractCodeModel::ParseNodelingVariables(const Node& node, NodelingType nodelingType)
{
@@ -5146,6 +5323,6 @@ namespace ScriptCanvas
return type == Data::eType::BehaviorContextObject;
}
}
}
}
}
@@ -387,6 +387,8 @@ namespace ScriptCanvas
void ParseMultiExecutionPre(ExecutionTreePtr execution);
void ParseMultipleFunctionCallPost(ExecutionTreePtr execution);
void ParseNodelingVariables(const Node& node, NodelingType nodelingType);
void ParseOperatorArithmetic(ExecutionTreePtr execution);
@@ -1132,14 +1132,14 @@ namespace ScriptCanvas
}
else if (variable->m_isExposedToConstruction)
{
if (variable->m_sourceVariableId.IsValid())
{
return VariableConstructionRequirement::InputVariable;
}
else if (variable->m_nodeableNodeId.IsValid())
if (variable->m_nodeableNodeId.IsValid())
{
return VariableConstructionRequirement::InputNodeable;
}
else if (variable->m_sourceVariableId.IsValid())
{
return VariableConstructionRequirement::InputVariable;
}
else
{
AZ_Assert(false, "A member variable in the model has no valid id");
@@ -164,6 +164,34 @@ namespace ScriptCanvas
virtual void PostParseExecutionTreeBody(AbstractCodeModel& /*model*/, ExecutionTreePtr /*execution*/) {}
};
// for now, no return values supported
struct MultipleFunctionCallFromSingleSlotEntry
{
AZ_TYPE_INFO(MultipleFunctionCallFromSingleSlotEntry, "{360A23A3-C490-4047-B71E-64E290E441D3}");
AZ_CLASS_ALLOCATOR(MultipleFunctionCallFromSingleSlotEntry, AZ::SystemAllocator, 0);
bool isVariadic = false;
AZStd::string functionName;
LexicalScope lexicalScope;
size_t numArguments = 0; // stride in case isVariadic == true
size_t startingIndex = 0; // the index of the slot order
};
// for now, no return values supported
struct MultipleFunctionCallFromSingleSlotInfo
{
AZ_TYPE_INFO(MultipleFunctionCallFromSingleSlotInfo, "{DF51F08A-8B28-4851-9888-9AB7CC0B90D2}");
AZ_CLASS_ALLOCATOR(MultipleFunctionCallFromSingleSlotInfo, AZ::SystemAllocator, 0);
// this could likely be implemented, but needs care to duplicate input that the execution-slot created
// bool errorOnReusedSlot = false;
bool errorOnUnusedSlot = false;
// calls are executed in the order they arrive in the vector
AZStd::vector<MultipleFunctionCallFromSingleSlotEntry> functionCalls;
};
struct NodeableParse
: public AZStd::enable_shared_from_this<NodeableParse>
{
@@ -313,6 +313,11 @@ namespace ScriptCanvas
return !m_returnValues.empty();
}
bool ExecutionTree::InputHasThisPointer() const
{
return m_inputHasThisPointer;
}
bool ExecutionTree::IsInfiniteLoopDetectionPoint() const
{
return m_isInfiniteLoopDetectionPoint;
@@ -377,6 +382,11 @@ namespace ScriptCanvas
m_isInfiniteLoopDetectionPoint = true;
}
void ExecutionTree::MarkInputHasThisPointer()
{
m_inputHasThisPointer = true;
}
void ExecutionTree::MarkInputOutputPreprocessed()
{
m_isInputOutputPreprocessed = true;
@@ -587,6 +597,32 @@ namespace ScriptCanvas
m_symbol = val;
}
}
void ExecutionTree::SwapChildren(ExecutionTreePtr execution)
{
if (execution)
{
m_children.swap(execution->m_children);
for (auto& child : m_children)
{
if (child.m_execution)
{
child.m_execution->SetParent(shared_from_this());
}
}
for (auto& orphan : execution->m_children)
{
if (orphan.m_execution)
{
orphan.m_execution->SetParent(execution);
}
}
}
else
{
ClearChildren();
}
}
}
}
@@ -190,6 +190,8 @@ namespace ScriptCanvas
bool HasReturnValues() const;
bool InputHasThisPointer() const;
bool IsInfiniteLoopDetectionPoint() const;
void InsertChild(size_t index, const ExecutionChild& child);
@@ -208,6 +210,8 @@ namespace ScriptCanvas
void MarkInfiniteLoopDetectionPoint();
void MarkInputHasThisPointer();
void MarkInputOutputPreprocessed();
void MarkInternalOut();
@@ -262,6 +266,8 @@ namespace ScriptCanvas
void SetSymbol(Symbol val);
void SwapChildren(ExecutionTreePtr execution);
private:
// the (possible) slot(s) through which execution exited, along with associated output
AZStd::vector<ExecutionChild> m_children;
@@ -275,6 +281,8 @@ namespace ScriptCanvas
bool m_isInfiniteLoopDetectionPoint = false;
bool m_inputHasThisPointer = false;
bool m_isInputOutputPreprocessed = false;
bool m_isInternalOut = false;
@@ -57,6 +57,12 @@ namespace ScriptCanvas
constexpr const char* MissingVariableForEBusHandlerAddress = "missing variable for ebus handler address";
constexpr const char* MissingVariableForEBusHandlerAddressConnected = "missing variable for ebus handler address";
constexpr const char* MultipleExecutionOutConnections = "This node has multiple, unordered execution Out connections";
constexpr const char* MultipleFunctionCallFromSingleSlotMultipleVariadic = "Only one variadic call (the last one) is supported in the multi-call per single slot.";
constexpr const char* MultipleFunctionCallFromSingleSlotNoChildren = "Node missing from parent children.";
constexpr const char* MultipleFunctionCallFromSingleSlotNotEnoughInput = "Not enough input to support multi call input information.";
constexpr const char* MultipleFunctionCallFromSingleSlotNotEnoughInputForThis = "Node doesn't have enough input for a parsed this pointer.";
constexpr const char* MultipleFunctionCallFromSingleSlotReused = "Multiple function slot reused an input slot";
constexpr const char* MultipleFunctionCallFromSingleSlotUnused = "Multiple function slot left an input slot unused.";
constexpr const char* MultipleSimulaneousInputValues = "Multiple values routed to the same single input with no way to discern which to take.";
constexpr const char* MultipleStartNodes = "Multiple Start nodes in a single graph. Only one is allowed.";
constexpr const char* NoChildrenAfterRoot = "No children after parsing function root";