2
0
Эх сурвалжийг харах

Add support for unordered_set to ScriptCanvas. Improved graph version upgrade systems and fixed related bugs.

chcurran 4 жил өмнө
parent
commit
c1e7970dbb
37 өөрчлөгдсөн 501 нэмэгдсэн , 408 устгасан
  1. 94 17
      Code/Framework/AzCore/AzCore/RTTI/AzStdOnDemandReflection.inl
  2. 3 3
      Code/Framework/AzCore/AzCore/RTTI/BehaviorContext.cpp
  3. 34 11
      Code/Framework/AzCore/AzCore/RTTI/BehaviorContextUtilities.cpp
  4. 2 0
      Code/Framework/AzCore/AzCore/RTTI/BehaviorContextUtilities.h
  5. 7 2
      Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/GenericComboBoxCtrl.h
  6. 34 73
      Gems/ScriptCanvas/Code/Editor/Components/EditorGraph.cpp
  7. 9 20
      Gems/ScriptCanvas/Code/Editor/Components/GraphUpgrade.cpp
  8. 3 2
      Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Components/EditorGraph.h
  9. 1 1
      Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Components/GraphUpgrade.h
  10. 5 0
      Gems/ScriptCanvas/Code/Editor/Static/Include/ScriptCanvas/View/EditCtrls/GenericLineEditCtrl.h
  11. 2 2
      Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasGrammar_Header.jinja
  12. 3 0
      Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Connection.h
  13. 15 12
      Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Contracts/MethodOverloadContract.cpp
  14. 28 1
      Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Graph.cpp
  15. 1 0
      Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Graph.h
  16. 3 1
      Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/PureData.h
  17. 5 0
      Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Slot.cpp
  18. 2 0
      Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Slot.h
  19. 13 11
      Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.cpp
  20. 0 9
      Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/BinaryOperator.cpp
  21. 0 1
      Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/BinaryOperator.h
  22. 0 184
      Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/ForEach.cpp
  23. 11 26
      Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/ForEach.h
  24. 5 0
      Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/FunctionCallNode.cpp
  25. 2 0
      Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/FunctionCallNode.h
  26. 22 1
      Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/FunctionDefinitionNode.cpp
  27. 5 3
      Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/FunctionDefinitionNode.h
  28. 5 2
      Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.cpp
  29. 4 0
      Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.h
  30. 39 10
      Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/MethodOverloaded.cpp
  31. 1 1
      Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/MethodOverloaded.h
  32. 0 8
      Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Operators/Operator.cpp
  33. 0 1
      Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Operators/Operator.h
  34. 2 2
      Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Time/Timer.ScriptCanvasGrammar.xml
  35. 110 0
      Gems/ScriptCanvas/Code/Include/ScriptCanvas/Utils/VersioningUtils.cpp
  36. 27 2
      Gems/ScriptCanvas/Code/Include/ScriptCanvas/Utils/VersioningUtils.h
  37. 4 2
      Gems/StartingPointInput/Code/Source/InputNode.ScriptCanvasGrammar.xml

+ 94 - 17
Code/Framework/AzCore/AzCore/RTTI/AzStdOnDemandReflection.inl

@@ -1020,29 +1020,60 @@ namespace AZ
         }
     };
 
-    /// OnDemand reflection for AZStd::set
+
+    template<class t_Key, class t_Hasher, class t_EqualKey, class t_Allocator>
+    class Iterator_VM<AZStd::unordered_set<t_Key, t_Hasher, t_EqualKey, t_Allocator>>
+    {
+    public:
+        using ContainerType = AZStd::unordered_set<t_Key, t_Hasher, t_EqualKey, t_Allocator>;
+        using IteratorType = typename ContainerType::iterator;
+        Iterator_VM(ContainerType& container)
+            : m_iterator(container.begin())
+            , m_end(container.end())
+        {}
+
+        const t_Key& GetKeyUnchecked() const
+        {
+            return *m_iterator;
+        }
+
+        bool IsNotAtEnd() const
+        {
+            return m_iterator != m_end;
+        }
+
+        t_Key& ModValueUnchecked()
+        {
+            return *m_iterator;
+        }
+
+        void Next()
+        {
+            ++m_iterator;
+        }
+
+    private:
+        IteratorType m_iterator;
+        IteratorType m_end;
+    };
+
+    /// OnDemand reflection for AZStd::unordered_set
     template<class Key, class Hasher, class EqualKey, class Allocator>
     struct OnDemandReflection< AZStd::unordered_set<Key, Hasher, EqualKey, Allocator> >
     {
         using ContainerType = AZStd::unordered_set<Key, Hasher, EqualKey, Allocator>;
         using KeyListType = AZStd::vector<Key, Allocator>;
 
-        static AZ::Outcome<void, void> Erase(ContainerType& thisMap, Key& key)
+        using ValueIteratorType = Iterator_VM<ContainerType>;
+
+        static bool EraseCheck_VM(ContainerType& thisSet, Key& key)
         {
-            const auto result = thisMap.erase(key);
-            if (result)
-            {
-                return AZ::Success();
-            }
-            else
-            {
-                return AZ::Failure();
-            }
+            return thisSet.erase(key) != 0;
         }
 
-        static void Insert(ContainerType& thisSet, Key& key)
+        static ContainerType& ErasePost_VM(ContainerType& thisSet, [[maybe_unused]] Key&)
         {
-            thisSet.insert(key);
+            return thisSet;
         }
 
         static KeyListType GetKeys(ContainerType& thisSet)
@@ -1055,6 +1086,17 @@ namespace AZ
             return keys;
         }
 
+        static ContainerType& Insert(ContainerType& thisSet, Key& key)
+        {
+            thisSet.insert(key);
+            return thisSet;
+        }
+
+        static ValueIteratorType Iterate_VM(ContainerType& thisContainer)
+        {
+            return ValueIteratorType(thisContainer);
+        }
+
         static void Swap(ContainerType& thisSet, ContainerType& otherSet)
         {
             thisSet.swap(otherSet);
@@ -1064,33 +1106,68 @@ namespace AZ
         {
             if (BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context))
             {
+                BranchOnResultInfo emptyBranchInfo;
+                emptyBranchInfo.m_returnResultInBranches = true;
+                emptyBranchInfo.m_trueToolTip = "The container is empty";
+                emptyBranchInfo.m_falseToolTip = "The container is not empty";
+
                 auto ContainsTransparent = [](const ContainerType& containerType, typename ContainerType::key_type& key)->bool
                 {
                     return containerType.contains(key);
                 };
 
+                ExplicitOverloadInfo explicitOverloadInfo;
                 behaviorContext->Class<ContainerType>()
-                    ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
+                    ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::ListOnly)
                     ->Attribute(AZ::ScriptCanvasAttributes::PrettyName, ScriptCanvasOnDemandReflection::OnDemandPrettyName<ContainerType>::Get(*behaviorContext))
                     ->Attribute(AZ::Script::Attributes::ToolTip, ScriptCanvasOnDemandReflection::OnDemandToolTip<ContainerType>::Get(*behaviorContext))
                     ->Attribute(AZ::Script::Attributes::Category, ScriptCanvasOnDemandReflection::OnDemandCategoryName<ContainerType>::Get(*behaviorContext))
                     ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::ScriptOwn)
                     ->Method("BucketCount", static_cast<typename ContainerType::size_type(ContainerType::*)() const>(&ContainerType::bucket_count))
-                    ->Method("Erase", &Erase)
-                    ->Method("Empty", [](ContainerType& thisSet)->bool { return thisSet.empty(); })
+                    ->Method("Empty", static_cast<bool(ContainerType::*)() const>(&ContainerType::empty), { { { "Container", "The container to check if it is empty", nullptr, {} } } })
+                        ->Attribute(AZ::ScriptCanvasAttributes::ExplicitOverloadCrc, ExplicitOverloadInfo("Is Empty", "Containers"))
+                        ->Attribute(AZ::ScriptCanvasAttributes::BranchOnResult, emptyBranchInfo)
+                    ->Method("EraseCheck_VM", &EraseCheck_VM)
+                        ->Attribute(AZ::Script::Attributes::TreatAsMemberFunction, AZ::AttributeIsValid::IfPresent)
+                        ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
+                    ->Method("Erase", &ErasePost_VM)
+                        ->Attribute(AZ::Script::Attributes::TreatAsMemberFunction, AZ::AttributeIsValid::IfPresent)
+                        ->Attribute(AZ::ScriptCanvasAttributes::ExplicitOverloadCrc, ExplicitOverloadInfo("Erase", "Containers"))
+                        ->Attribute(AZ::ScriptCanvasAttributes::CheckedOperation, CheckedOperationInfo("EraseCheck_VM", {}, "Out", "Key Not Found", true))
+                        ->Attribute(AZ::ScriptCanvasAttributes::OverloadArgumentGroup, AZ::OverloadArgumentGroupInfo({ "ContainerGroup", "" }, { "ContainerGroup" }))
                     ->Method("contains", ContainsTransparent)
+                        ->Attribute(AZ::ScriptCanvasAttributes::ExplicitOverloadCrc, ExplicitOverloadInfo("Has Key", "Containers"))
                         ->Attribute(AZ::Script::Attributes::TreatAsMemberFunction, AZ::AttributeIsValid::IfPresent)
                     ->Method("Insert", &Insert)
+                        ->Attribute(AZ::Script::Attributes::TreatAsMemberFunction, AZ::AttributeIsValid::IfPresent)
+                        ->Attribute(AZ::ScriptCanvasAttributes::ExplicitOverloadCrc, ExplicitOverloadInfo("Insert", "Containers"))
+                        ->Attribute(AZ::ScriptCanvasAttributes::OverloadArgumentGroup, AZ::OverloadArgumentGroupInfo({ "ContainerGroup", "", "" }, { "ContainerGroup" }))
                     ->Method(k_sizeName, [](ContainerType* thisPtr) { return aznumeric_cast<int>(thisPtr->size()); })
                         ->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Length)
                     ->Method("GetKeys", &GetKeys)
                         ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
+                    ->Method("GetSize", [](ContainerType& thisPtr) { return aznumeric_cast<int>(thisPtr.size()); })
+                        ->Attribute(AZ::ScriptCanvasAttributes::ExplicitOverloadCrc, ExplicitOverloadInfo("Get Size", "Containers"))
+                        ->Attribute(AZ::Script::Attributes::TreatAsMemberFunction, AZ::AttributeIsValid::IfPresent)
                     ->Method("Reserve", static_cast<void(ContainerType::*)(typename ContainerType::size_type)>(&ContainerType::reserve))
                     ->Method("Swap", &Swap)
+                    ->Method("Clear", [](ContainerType& thisContainer)->ContainerType& { thisContainer.clear(); return thisContainer; })
+                        ->Attribute(AZ::Script::Attributes::TreatAsMemberFunction, AZ::AttributeIsValid::IfPresent)
+                        ->Attribute(AZ::ScriptCanvasAttributes::ExplicitOverloadCrc, ExplicitOverloadInfo("Clear All Elements", "Containers"))
+                        ->Attribute(AZ::ScriptCanvasAttributes::OverloadArgumentGroup, AZ::OverloadArgumentGroupInfo({ "ContainerGroup" }, { "ContainerGroup" }))
+                    ->Method(k_iteratorConstructorName, &Iterate_VM)
+                        ;
+
+                behaviorContext->Class<ValueIteratorType>()
+                    ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::ListOnly)
+                    ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::ScriptOwn)
+                    ->Method(k_iteratorGetKeyName, &ValueIteratorType::GetKeyUnchecked)
+                    ->Method(k_iteratorModValueName, &ValueIteratorType::ModValueUnchecked)
+                    ->Method(k_iteratorIsNotAtEndName, &ValueIteratorType::IsNotAtEnd)
+                    ->Method(k_iteratorNextName, &ValueIteratorType::Next)
                     ;
             }
         }
-
     };
 
     template <>

+ 3 - 3
Code/Framework/AzCore/AzCore/RTTI/BehaviorContext.cpp

@@ -165,7 +165,7 @@ namespace AZ
 
         if (HasResult() != overload->HasResult())
         {
-            AZ_Error("Reflection", false, "Overload failure, all methods must have the same result, or none at all");
+            AZ_Error("Reflection", false, "Overload failure, all methods must have the same result, or none at all: %s", m_name.c_str());
             return false;
         }
 
@@ -176,7 +176,7 @@ namespace AZ
 
             if (!(methodResult->m_typeId == overloadResult->m_typeId && methodResult->m_traits == overloadResult->m_traits))
             {
-                AZ_Error("Reflection", false, "Overload failure, all methods must have the same result, or none at all");
+                AZ_Error("Reflection", false, "Overload failure, all methods must have the same result, or none at all: %s", m_name.c_str());
                 return false;
             }
         }
@@ -575,7 +575,7 @@ namespace AZ
                 }
                 else
                 {
-                    AZ_Error("BehaviorContext", false, "safety check declared for method %s but it was not found in the class");
+                    AZ_Error("BehaviorContext", false, "Method: %s, declared safety check: %s, but it was not found in class: %s", method.m_name.c_str(), m_name.c_str(), checkedOperationInfo.m_safetyCheckName.c_str());
                 }
             }
         }

+ 34 - 11
Code/Framework/AzCore/AzCore/RTTI/BehaviorContextUtilities.cpp

@@ -34,10 +34,17 @@ namespace BehaviorContextUtilitiesCPP
         using argument_type = const BehaviorParameter*;
         using result_type = size_t;
         result_type operator()(const argument_type& value) const 
-        { 
-            result_type result = AZStd::hash<Uuid>()(value->m_typeId);
-            AZStd::hash_combine(result, CleanTraits(value->m_traits));
-            return result;
+        {
+            if (value)
+            { 
+                result_type result = AZStd::hash<Uuid>()(value->m_typeId);
+                AZStd::hash_combine(result, CleanTraits(value->m_traits));
+                return result;
+            }
+            else
+            {
+                return 0;
+            }
         }
     };
 
@@ -45,7 +52,11 @@ namespace BehaviorContextUtilitiesCPP
     {
         bool operator()(const BehaviorParameter* left, const BehaviorParameter* right) const
         {
-            return left->m_typeId == right->m_typeId && CleanTraits(left->m_traits) == CleanTraits(right->m_traits);
+            return (left == nullptr && right == nullptr)
+                || (left != nullptr
+                    && right != nullptr
+                    && left->m_typeId == right->m_typeId
+                    && CleanTraits(left->m_traits) == CleanTraits(right->m_traits));
         }
     };
     
@@ -137,7 +148,7 @@ namespace AZ
         for (size_t argIndex = 0, argSentinel = overload.GetNumArguments(); argIndex < argSentinel; ++argIndex)
         {
             auto overloadedArgIter = variance.m_input.find(argIndex);
-            if (overloadedArgIter != variance.m_input.end())
+            if (overloadedArgIter != variance.m_input.end() && overloadedArgIter->second[overloadIndex])
             {
                 // if this doesn't work try the type name
                 overloadName += ReplaceCppArtifacts(overloadedArgIter->second[overloadIndex]->m_name);
@@ -185,16 +196,24 @@ namespace AZ
             {
                 auto argument = overloads[overloadIndex].first->GetArgument(0);
 
-                const bool isThisPointer
-                    = (argument->m_traits & AZ::BehaviorParameter::Traits::TR_THIS_PTR) != 0
-                    || AZ::FindAttribute(AZ::Script::Attributes::TreatAsMemberFunction, overloads[overloadIndex].first->m_attributes);
+                if (argument)
+                {
+                    const bool isThisPointer
+                        = (argument->m_traits & AZ::BehaviorParameter::Traits::TR_THIS_PTR) != 0
+                        || AZ::FindAttribute(AZ::Script::Attributes::TreatAsMemberFunction, overloads[overloadIndex].first->m_attributes);
 
-                oneArgIsThisPointer = oneArgIsThisPointer || isThisPointer;
+                    oneArgIsThisPointer = oneArgIsThisPointer || isThisPointer;
+                }
 
                 types.insert(argument);
                 stripedArgs.emplace_back(argument);
             }
 
+            if (types.size() == overloads.size())
+            {
+                variance.m_unambiguousInput.insert(0);
+            }
+
             if (types.size() > 1 && (onThis == VariantOnThis::Yes || !oneArgIsThisPointer))
             {
                 variance.m_input.insert(AZStd::make_pair(0, stripedArgs));
@@ -210,11 +229,15 @@ namespace AZ
             for (size_t overloadIndex = 0, overloadSentinel = overloads.size(); overloadIndex < overloadSentinel; ++overloadIndex)
             {
                 auto argument = overloads[overloadIndex].first->GetArgument(argIndex);
-
                 types.insert(argument);
                 stripedArgs.emplace_back(argument);
             }
 
+            if (types.size() == overloads.size())
+            {
+                variance.m_unambiguousInput.insert(0);
+            }
+
             if (types.size() > 1)
             {
                 variance.m_input.insert(AZStd::make_pair(argIndex, stripedArgs));

+ 2 - 0
Code/Framework/AzCore/AzCore/RTTI/BehaviorContextUtilities.h

@@ -27,6 +27,8 @@ namespace AZ
     struct OverloadVariance
     {
         AZStd::unordered_map<size_t, AZStd::vector<const BehaviorParameter*>> m_input;
+        // the indices of inputs that make selection of overload unambiguous
+        AZStd::unordered_set<size_t> m_unambiguousInput;
         AZStd::vector<const BehaviorParameter*> m_output;
     };
 

+ 7 - 2
Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/GenericComboBoxCtrl.h

@@ -83,7 +83,7 @@ namespace AzToolsFramework
     protected:
         QWidget* GetFirstInTabOrder() override;
         QWidget* GetLastInTabOrder() override;
-        void UpdateTabOrder() override;        
+        void UpdateTabOrder() override;
 
         void onChildComboBoxValueChange(int comboBoxIndex) override;
 
@@ -93,7 +93,7 @@ namespace AzToolsFramework
 
         void addElementImpl(const AZStd::pair<T, AZStd::string>& genericValue);
 
-        QLabel*    m_warningLabel = nullptr;
+        QLabel* m_warningLabel = nullptr;
         DHQComboBox* m_pComboBox;
         AZStd::vector<AZStd::pair<T, AZStd::string>> m_values;
         AZ::AttributeFunction <void(const T&)>* m_postChangeNotifyCB{};
@@ -131,6 +131,11 @@ namespace AzToolsFramework
     template<typename T>
     AzToolsFramework::PropertyHandlerBase* RegisterGenericComboBoxHandler()
     {
+        if (!AzToolsFramework::PropertyTypeRegistrationMessages::Bus::FindFirstHandler())
+        {
+            return nullptr;
+        }
+
         auto propertyHandler = aznew GenericComboBoxHandler<T>();
         AzToolsFramework::PropertyTypeRegistrationMessages::Bus::Broadcast(&AzToolsFramework::PropertyTypeRegistrationMessages::RegisterPropertyType, propertyHandler);
         return propertyHandler;

+ 34 - 73
Gems/ScriptCanvas/Code/Editor/Components/EditorGraph.cpp

@@ -512,7 +512,7 @@ namespace ScriptCanvasEditor
         }
     }
 
-    bool Graph::SanityCheckNodeReplacement(ScriptCanvas::Node* oldNode, ScriptCanvas::Node* newNode, AZStd::unordered_map<ScriptCanvas::SlotId, AZStd::vector<ScriptCanvas::SlotId>>& outSlotIdMap)
+    bool Graph::SanityCheckNodeReplacement(ScriptCanvas::Node* oldNode, ScriptCanvas::Node* newNode, ScriptCanvas::NodeUpdateSlotReport& nodeUpdateSlotReport)
     {
         auto findReplacementMatch = [](const ScriptCanvas::Slot* oldSlot, const AZStd::vector<const ScriptCanvas::Slot*>& newSlots)->ScriptCanvas::SlotId
         {
@@ -529,14 +529,14 @@ namespace ScriptCanvasEditor
             return {};
         };
 
-        oldNode->CustomizeReplacementNode(newNode, outSlotIdMap);
-
         if (!newNode)
         {
             AZ_Warning("ScriptCanvas", false, "Replacement node can not be null.");
             return false;
         }
 
+        oldNode->CustomizeReplacementNode(newNode, nodeUpdateSlotReport.m_oldSlotsToNewSlots);
+
         AZStd::unordered_map<AZStd::string, AZStd::vector<AZStd::string>> slotNameMap = oldNode->GetReplacementSlotsMap();
 
         const auto newSlots = newNode->GetAllSlots();
@@ -544,16 +544,19 @@ namespace ScriptCanvasEditor
         bool usingDefaults = true;
         size_t defaultMatchesFound = 0;
 
+        auto& oldSlotsToNewSlots = nodeUpdateSlotReport.m_oldSlotsToNewSlots;
+
         for (auto oldSlot : oldSlots)
         {
             const ScriptCanvas::SlotId oldSlotId = oldSlot->GetId();
             const AZStd::string oldSlotName = oldSlot->GetName();
-            auto slotIdsIter = outSlotIdMap.find(oldSlotId);
+
+            auto slotIdsIter = oldSlotsToNewSlots.find(oldSlotId);
             auto slotNamesIter = slotNameMap.find(oldSlotName);
             // For old node slot remapping, we should get:
             // 1. if old slot name is not static, we should find the mapping in user provided slot id map
             // 2. if old slot name is static, we should find the mapping in codegen generated map (case 1 can override case 2)
-            if (slotIdsIter != outSlotIdMap.end())
+            if (slotIdsIter != oldSlotsToNewSlots.end())
             {
                 for (auto newSlotId : slotIdsIter->second)
                 {
@@ -581,9 +584,10 @@ namespace ScriptCanvasEditor
                     if (!newSlotName.empty())
                     {
                         auto newSlot = newNode->GetSlotByName(newSlotName);
+
                         if (!newSlot)
                         {
-                            AZ_Warning("ScriptCanvas", false, "Failed to find slot with name %s in replacement Node(%s).", newSlotName.c_str(), newNode->GetNodeName().c_str());
+                            AZ_Warning("ScriptCanvas", false, "Failed to find slot with name %s in replacement Node (%s).", newSlotName.c_str(), newNode->GetNodeName().c_str());
                             return false;
                         }
                         else if (newSlot && oldSlot->GetType() != newSlot->GetType())
@@ -591,10 +595,11 @@ namespace ScriptCanvasEditor
                             AZ_Warning("ScriptCanvas", false, "Failed to map deprecated Node (%s) Slot (%s) to replacement Node (%s) Slot (%s).", oldNode->GetNodeName().c_str(), oldSlot->GetName().c_str(), newNode->GetNodeName().c_str(), newSlot->GetName().c_str());
                             return false;
                         }
+
                         newSlotIds.push_back(newSlot->GetId());
                     }
                 }
-                outSlotIdMap.emplace(oldSlot->GetId(), newSlotIds);
+                oldSlotsToNewSlots.emplace(oldSlot->GetId(), newSlotIds);
             }
             else if (slotNameMap.empty())
             {
@@ -605,7 +610,7 @@ namespace ScriptCanvasEditor
                 {
                     ++defaultMatchesFound;
                     AZStd::vector<ScriptCanvas::SlotId> slotIds{ newSlotId };
-                    outSlotIdMap.emplace(oldSlot->GetId(), slotIds);
+                    oldSlotsToNewSlots.emplace(oldSlot->GetId(), slotIds);
                 }
             }
             else
@@ -615,7 +620,7 @@ namespace ScriptCanvasEditor
             }
         }
 
-        if (usingDefaults && defaultMatchesFound != oldSlots.size())
+        if (usingDefaults && oldSlotsToNewSlots.size() != oldSlots.size())
         {
             AZ_Warning("ScriptCanvas", false, "Failed to remap deprecated Node(%s) not all old slots were present in the new node.", oldNode->GetNodeName().c_str());
         }
@@ -690,8 +695,10 @@ namespace ScriptCanvasEditor
         }
     }
 
-    AZ::Outcome<ScriptCanvas::Node*> Graph::ReplaceNodeByConfig(ScriptCanvas::Node* oldNode, const ScriptCanvas::NodeConfiguration& nodeConfig,
-        ScriptCanvas::ReplacementConnectionMap& remapConnections)
+    AZ::Outcome<ScriptCanvas::Node*> Graph::ReplaceNodeByConfig
+        ( ScriptCanvas::Node* oldNode
+        , const ScriptCanvas::NodeConfiguration& nodeConfig
+        , ScriptCanvas::NodeUpdateSlotReport& nodeUpdateSlotReport)
     {
         auto nodeEntity = oldNode->GetEntity();
         if (!nodeEntity)
@@ -731,8 +738,8 @@ namespace ScriptCanvasEditor
         AddNode(newNode->GetEntityId());
         ScriptCanvas::NodeUtils::InitializeNode(newNode, nodeConfig);
 
-        AZStd::unordered_map<ScriptCanvas::SlotId, AZStd::vector<ScriptCanvas::SlotId>> slotIdMap;
-        rollbackRequired = !SanityCheckNodeReplacement(oldNode, newNode, slotIdMap);
+        rollbackRequired = !SanityCheckNodeReplacement(oldNode, newNode, nodeUpdateSlotReport);
+        auto& slotIdMap = nodeUpdateSlotReport.m_oldSlotsToNewSlots;
 
         if (rollbackRequired)
         {
@@ -748,25 +755,15 @@ namespace ScriptCanvasEditor
         else
         {
             nodeEntity->Activate();
-
             newNode->SignalReconfigurationBegin();
-
             newNode->SetNodeDisabledFlag(oldNode->GetNodeDisabledFlag());
+
             for (auto slotIdIter : slotIdMap)
             {
                 ScriptCanvas::Slot* oldSlot = oldNode->GetSlot(slotIdIter.first);
                 const ScriptCanvas::Endpoint oldEndpoint{ nodeEntity->GetId(), oldSlot->GetId() };
                 if (slotIdIter.second.size() == 0)
                 {
-                    // If remap id is empty, then we should just cache the old slot connection for delete
-                    if (oldSlot->IsInput())
-                    {
-                        ScriptCanvas::VersioningUtils::CreateRemapConnectionsForTargetEndpoint(*this, oldEndpoint, ScriptCanvas::Endpoint(), remapConnections);
-                    }
-                    else
-                    {
-                        ScriptCanvas::VersioningUtils::CreateRemapConnectionsForSourceEndpoint(*this, oldEndpoint, ScriptCanvas::Endpoint(), remapConnections);
-                    }
                     continue;
                 }
 
@@ -808,35 +805,11 @@ namespace ScriptCanvasEditor
 
                         ScriptCanvas::VersioningUtils::CopyOldValueToDataSlot(newSlot, oldSlot->GetVariableReference(), oldSlot->FindDatum());
                     }
-
-                    // if old slot is visible, we need to check its connections for remapping
-                    if (oldSlot->IsVisible())
-                    {
-                        ScriptCanvas::Endpoint newEndpoint;
-                        if (newSlot)
-                        {
-                            newEndpoint = { nodeEntity->GetId(), newSlot->GetId() };
-                        }
-                        else
-                        {
-                            AZ_Warning("ScriptCanvas", false, "Invalid slot! Unable to create new connection for Node (%s).", newNode->GetNodeName().c_str());
-                        }
-
-                        if (oldSlot->IsInput())
-                        {
-                            ScriptCanvas::VersioningUtils::CreateRemapConnectionsForTargetEndpoint(*this, oldEndpoint, newEndpoint, remapConnections);
-                        }
-                        else
-                        {
-                            ScriptCanvas::VersioningUtils::CreateRemapConnectionsForSourceEndpoint(*this, oldEndpoint, newEndpoint, remapConnections);
-                        }
-                    }
                 }
             }
-            delete oldNode;
 
+            delete oldNode;
             newNode->SignalReconfigurationEnd();
-
             return AZ::Success(newNode);
         }
     }
@@ -3634,8 +3607,7 @@ namespace ScriptCanvasEditor
 
         AZStd::unordered_map< AZ::EntityId, AZ::EntityId > scriptCanvasToGraphCanvasMapping;
 
-        bool graphNeedsDirtying = false;
-
+        bool graphNeedsDirtying = !GetVersion().IsLatest();
         {
             QScopedValueRollback<bool> ignoreRequests(m_ignoreSaveRequests, true);
 
@@ -3659,7 +3631,8 @@ namespace ScriptCanvasEditor
             AZStd::unordered_set<AZ::EntityId> deletedNodes;
             AZStd::unordered_set<AZ::EntityId> assetSanitizationSet;
             AZStd::unordered_set<ScriptCanvas::Node*> sanityCheckRequiredNodes;
-            ScriptCanvas::ReplacementConnectionMap remapConnections;
+
+            ScriptCanvas::GraphUpdateSlotReport graphUpdateSlotReport;
 
             for (const AZ::EntityId& scriptCanvasNodeId : nodeList)
             {
@@ -3674,12 +3647,15 @@ namespace ScriptCanvasEditor
                         ScriptCanvas::NodeConfiguration nodeConfig = scriptCanvasNode->GetReplacementNodeConfiguration();
                         if (nodeConfig.IsValid())
                         {
-                            auto nodeOutcome = ReplaceNodeByConfig(scriptCanvasNode, nodeConfig, remapConnections);
+                            ScriptCanvas::NodeUpdateSlotReport nodeUpdateSlotReport;
+                            auto nodeOutcome = ReplaceNodeByConfig(scriptCanvasNode, nodeConfig, nodeUpdateSlotReport);
+
                             if (nodeOutcome.IsSuccess())
                             {
                                 graphNeedsDirtying = true;
                                 scriptCanvasNode = nodeOutcome.GetValue();
                                 m_updateStrings.insert(AZStd::string::format("Replaced node (%s)", scriptCanvasNode->GetNodeName().c_str()));
+                                ScriptCanvas::MergeUpdateSlotReport(scriptCanvasNodeId, graphUpdateSlotReport, nodeUpdateSlotReport);
                             }
                         }
                     }
@@ -3688,7 +3664,6 @@ namespace ScriptCanvasEditor
                     scriptCanvasToGraphCanvasMapping[scriptCanvasNodeId] = graphCanvasNodeId;
 
                     auto saveDataIter2 = m_graphCanvasSaveData.find(scriptCanvasNodeId);
-
                     if (saveDataIter2 != m_graphCanvasSaveData.end())
                     {
                         GraphCanvas::EntitySaveDataRequestBus::Event(graphCanvasNodeId, &GraphCanvas::EntitySaveDataRequests::ReadSaveData, (*saveDataIter2->second));
@@ -3699,7 +3674,7 @@ namespace ScriptCanvasEditor
 
                     GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::AddNode, graphCanvasNodeId, position, false);
 
-                    // If the node is deprecated, we want to stomp whatever style it had saved and apply the deperecated style
+                    // If the node is deprecated, we want to stomp whatever style it had saved and apply the deprecated style
                     if (scriptCanvasNode->IsDeprecated())
                     {
                         GraphCanvas::NodeTitleRequestBus::Event(graphCanvasNodeId, &GraphCanvas::NodeTitleRequests::SetPaletteOverride, "DeprecatedNodeTitlePalette");
@@ -3719,26 +3694,12 @@ namespace ScriptCanvasEditor
                 }
             }
 
-            // Remap connections step should be done before editor processing connections for graph
-            //
-            // Delete underlying data conenections.
-            for (auto remapConnection : remapConnections)
-            {
-                RemoveConnection(remapConnection.first);
-            }
-
-            // Recreate connections in a separate pass to avoid triggering display updates for invalid slot ids.
-            for (auto remapConnection : remapConnections)
+            if (!graphUpdateSlotReport.IsEmpty())
             {
-                for (auto newEndpointPair : remapConnection.second)
-                {
-                    if (newEndpointPair.first.IsValid() && newEndpointPair.second.IsValid())
-                    {
-                        ConnectByEndpoint(newEndpointPair.first, newEndpointPair.second);
-                    }
-                }
+                // currently, it is expected that there are no deleted old slots, those need manual correction
+                AZ_Error("ScriptCanvas", graphUpdateSlotReport.m_deletedOldSlots.empty(), "Graph upgrade path: If old slots are deleted, manual upgrading is required");
+                UpdateConnectionStatus(*this, graphUpdateSlotReport);
             }
-            ////
 
             AZStd::unordered_set<AZ::EntityId> graphCanvasNodesToDelete;
 

+ 9 - 20
Gems/ScriptCanvas/Code/Editor/Components/GraphUpgrade.cpp

@@ -424,24 +424,11 @@ namespace ScriptCanvasEditor
         EditorGraphUpgradeMachine* sm = GetStateMachine<EditorGraphUpgradeMachine>();
         auto* graph = sm->m_graph;
 
-        // Delete underlying data connections.
-        for (auto remapConnection : sm->m_replacementConnections)
+        if (!sm->m_updateReport.IsEmpty())
         {
-            graph->RemoveConnection(remapConnection.first);
-        }
-
-        // Recreate connections in a separate pass to avoid triggering display updates for invalid slot ids.
-        for (auto remapConnection : sm->m_replacementConnections)
-        {
-            for (auto newEndpointPair : remapConnection.second)
-            {
-                if (newEndpointPair.first.IsValid() && newEndpointPair.second.IsValid())
-                {
-                    graph->ConnectByEndpoint(newEndpointPair.first, newEndpointPair.second);
-
-                    Log("Replaced Connection: %s\n", Helpers::ConnectionToText(graph, newEndpointPair.first, newEndpointPair.second).c_str());
-                }
-            }
+            // currently, it is expected that there are no deleted old slots, those need manual correction
+            AZ_Error("ScriptCanvas", sm->m_updateReport.m_deletedOldSlots.empty(), "Graph upgrade path: If old slots are deleted, manual upgrading is required");
+            UpdateConnectionStatus(*graph, sm->m_updateReport);
         }
     }
 
@@ -455,16 +442,18 @@ namespace ScriptCanvasEditor
             ScriptCanvas::NodeConfiguration nodeConfig = node->GetReplacementNodeConfiguration();
             if (nodeConfig.IsValid())
             {
-                auto nodeOutcome = graph->ReplaceNodeByConfig(node, nodeConfig, sm->m_replacementConnections);
+                ScriptCanvas::NodeUpdateSlotReport nodeUpdateSlotReport;
+                auto nodeOutcome = graph->ReplaceNodeByConfig(node, nodeConfig, nodeUpdateSlotReport);
                 if (nodeOutcome.IsSuccess())
                 {
+                    ScriptCanvas::MergeUpdateSlotReport(node->GetEntityId(), sm->m_updateReport, nodeUpdateSlotReport);
+
                     sm->m_allNodes.erase(node);
                     sm->m_outOfDateNodes.erase(node);
                     sm->m_sanityCheckRequiredNodes.erase(node);
-
                     sm->m_graphNeedsDirtying = true;
-                    auto replacedNode = nodeOutcome.GetValue();
 
+                    auto replacedNode = nodeOutcome.GetValue();
                     sm->m_allNodes.insert(replacedNode);
 
                     if (replacedNode->IsOutOfDate(graph->GetVersion()))

+ 3 - 2
Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Components/EditorGraph.h

@@ -42,6 +42,7 @@
 namespace ScriptCanvas
 {
     struct NodeConfiguration;
+    struct NodeUpdateSlotReport;
 }
 
 namespace ScriptCanvasEditor
@@ -361,8 +362,8 @@ namespace ScriptCanvasEditor
         void HandleFunctionDefinitionExtension(ScriptCanvas::Node* node, GraphCanvas::SlotId graphCanvasSlotId, const GraphCanvas::NodeId& nodeId);
 
         //// Version Update code
-        AZ::Outcome<ScriptCanvas::Node*> ReplaceNodeByConfig(ScriptCanvas::Node*, const ScriptCanvas::NodeConfiguration&, ScriptCanvas::ReplacementConnectionMap&);
-        bool SanityCheckNodeReplacement(ScriptCanvas::Node*, ScriptCanvas::Node*, AZStd::unordered_map<ScriptCanvas::SlotId, AZStd::vector<ScriptCanvas::SlotId>>&);
+        AZ::Outcome<ScriptCanvas::Node*> ReplaceNodeByConfig(ScriptCanvas::Node*, const ScriptCanvas::NodeConfiguration&, ScriptCanvas::NodeUpdateSlotReport& nodeUpdateSlotReport);
+        bool SanityCheckNodeReplacement(ScriptCanvas::Node*, ScriptCanvas::Node*, ScriptCanvas::NodeUpdateSlotReport& nodeUpdateSlotReport);
 
         bool m_allowVersionUpdate = false;
         AZStd::unordered_set< AZ::EntityId > m_queuedConvertingNodes;

+ 1 - 1
Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Components/GraphUpgrade.h

@@ -159,7 +159,7 @@ namespace ScriptCanvasEditor
 
         AZStd::unordered_set<AZ::EntityId> m_deletedNodes;
         AZStd::unordered_set<AZ::EntityId> m_assetSanitizationSet;
-        ScriptCanvas::ReplacementConnectionMap m_replacementConnections;
+        ScriptCanvas::GraphUpdateSlotReport m_updateReport;
 
         AZStd::unordered_map< AZ::EntityId, AZ::EntityId > m_scriptCanvasToGraphCanvasMapping;
 

+ 5 - 0
Gems/ScriptCanvas/Code/Editor/Static/Include/ScriptCanvas/View/EditCtrls/GenericLineEditCtrl.h

@@ -123,6 +123,11 @@ namespace ScriptCanvasEditor
     template<typename T>
     AzToolsFramework::PropertyHandlerBase* RegisterGenericLineEditHandler(const EditCtrl::PropertyToStringCB<T>& propertyToStringCB, const EditCtrl::StringToPropertyCB<T>& stringToPropertyCB)
     {
+        if (!AzToolsFramework::PropertyTypeRegistrationMessages::Bus::FindFirstHandler())
+        {
+            return nullptr;
+        }
+
         auto propertyHandler(aznew GenericLineEditHandler<T>(propertyToStringCB, stringToPropertyCB));
         AzToolsFramework::PropertyTypeRegistrationMessages::Bus::Broadcast(&AzToolsFramework::PropertyTypeRegistrationMessages::RegisterPropertyType, propertyHandler);
         return propertyHandler;

+ 2 - 2
Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasGrammar_Header.jinja

@@ -75,7 +75,7 @@ public: \
     void ConfigureSlots() override; \
     bool RequiresDynamicSlotOrdering() const override; \
     bool IsDeprecated() const override; \
-{% if deprecationUuid is defined %}    NodeConfiguration GetReplacementNodeConfiguration() const override; \
+{% if deprecationUuid is defined %}    ScriptCanvas::NodeConfiguration GetReplacementNodeConfiguration() const override; \
 {% endif %}
     using Node::FindDatum; \
 {% if Class.attrib['GraphEntryPoint'] is defined %}    bool IsEntryPoint() const override { return {%if Class.attrib['GraphEntryPoint'] == "True" %}true{%else%}false{%endif%}; } \
@@ -168,4 +168,4 @@ struct {{ className | replace(' ','') }}Property
 
 
 {%     endfor %}
-{% endfor %}
+{% endfor %}

+ 3 - 0
Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Connection.h

@@ -22,6 +22,7 @@
 namespace ScriptCanvas
 {
     class Slot;
+    struct NodeUpdateSlotReport;
 
     class Connection
         : public AZ::Component
@@ -64,6 +65,8 @@ namespace ScriptCanvas
         // GraphNotificationBus
         void OnNodeRemoved(const ID& nodeId) override;
 
+        void UpdateConnectionStatus(NodeUpdateSlotReport& report);
+
     protected:
         //-------------------------------------------------------------------------
         static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)

+ 15 - 12
Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Contracts/MethodOverloadContract.cpp

@@ -33,7 +33,7 @@ namespace ScriptCanvas
     {
         if (m_availableIndexes.empty())
         {
-            return -1;
+            return std::numeric_limits<AZ::u32>::max();
         }
 
         return (*m_availableIndexes.begin());
@@ -151,19 +151,22 @@ namespace ScriptCanvas
 
             for (const AZ::BehaviorParameter* behaviorParameter : paramTypes.second)
             {
-                ScriptCanvas::Data::Type dataType = ScriptCanvas::Data::FromAZType(behaviorParameter->m_typeId);
-                if (ScriptCanvas::Data::IsValueType(dataType))
+                if (behaviorParameter)
                 {
-                    isValueType = true;
-                }
-                else if (ScriptCanvas::Data::IsContainerType(dataType))
-                {
-                    isContainerType = true;
-                }
+                    ScriptCanvas::Data::Type dataType = ScriptCanvas::Data::FromAZType(behaviorParameter->m_typeId);
+                    if (ScriptCanvas::Data::IsValueType(dataType))
+                    {
+                        isValueType = true;
+                    }
+                    else if (ScriptCanvas::Data::IsContainerType(dataType))
+                    {
+                        isContainerType = true;
+                    }
 
-                if (isValueType && isContainerType)
-                {
-                    break;
+                    if (isValueType && isContainerType)
+                    {
+                        break;
+                    }
                 }
             }
 

+ 28 - 1
Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Graph.cpp

@@ -616,6 +616,34 @@ namespace ScriptCanvas
         return false;
     }
 
+
+    void Graph::RemoveAllConnections()
+    {
+        for (auto connectionEntity : m_graphData.m_connections)
+        {
+            if (auto connection = connectionEntity ? AZ::EntityUtils::FindFirstDerivedComponent<Connection>(connectionEntity) : nullptr)
+            {
+                if (connection->GetSourceEndpoint().IsValid())
+                {
+                    EndpointNotificationBus::Event(connection->GetSourceEndpoint(), &EndpointNotifications::OnEndpointDisconnected, connection->GetTargetEndpoint());
+                }
+                if (connection->GetTargetEndpoint().IsValid())
+                {
+                    EndpointNotificationBus::Event(connection->GetTargetEndpoint(), &EndpointNotifications::OnEndpointDisconnected, connection->GetSourceEndpoint());
+                }
+            }
+
+            GraphNotificationBus::Event(GetScriptCanvasId(), &GraphNotifications::OnConnectionRemoved, connectionEntity->GetId());
+        }
+
+        for (auto& connectionRef : m_graphData.m_connections)
+        {
+            delete connectionRef;
+        }
+
+        m_graphData.m_connections.clear();
+    }
+
     bool Graph::RemoveConnection(const AZ::EntityId& connectionId)
     {
         if (connectionId.IsValid())
@@ -752,7 +780,6 @@ namespace ScriptCanvas
             auto* connectionEntity = aznew AZ::Entity("Connection");
             connectionEntity->CreateComponent<Connection>(sourceEndpoint, targetEndpoint);
 
-
             AZ::Entity* nodeEntity{};
             AZ::ComponentApplicationBus::BroadcastResult(nodeEntity, &AZ::ComponentApplicationRequests::FindEntity, sourceEndpoint.GetNodeId());
             auto node = nodeEntity ? AZ::EntityUtils::FindFirstDerivedComponent<Node>(nodeEntity) : nullptr;

+ 1 - 0
Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Graph.h

@@ -87,6 +87,7 @@ namespace ScriptCanvas
         Slot* FindSlot(const Endpoint& endpoint) const override;
 
         bool AddConnection(const AZ::EntityId&) override;
+        void RemoveAllConnections();
         bool RemoveConnection(const AZ::EntityId& connectionId) override;
         AZStd::vector<AZ::EntityId> GetConnections() const override;
         AZStd::vector<Endpoint> GetConnectedEndpoints(const Endpoint& firstEndpoint) const override;

+ 3 - 1
Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/PureData.h

@@ -53,7 +53,9 @@ namespace ScriptCanvas
         template<typename DatumType>
         void AddDefaultInputAndOutputTypeSlot(DatumType&& defaultValue);
         void AddInputTypeAndOutputTypeSlot(const Data::Type& type);
-        
+
+        bool IsDeprecated() const override { return true; }
+
         void OnActivate() override;
         void OnInputChanged(const Datum& input, const SlotId& id) override;
         void MarkDefaultableInput() override {}

+ 5 - 0
Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Slot.cpp

@@ -347,6 +347,11 @@ namespace ScriptCanvas
         }
     }    
 
+    void Slot::ClearDynamicGroup()
+    {
+        m_dynamicGroup = AZ::Crc32{};
+    }
+
     void Slot::ConvertToLatentExecutionOut()
     {
         if (IsExecution() && IsOutput())

+ 2 - 0
Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Slot.h

@@ -68,6 +68,8 @@ namespace ScriptCanvas
 
         void AddContract(const ContractDescriptor& contractDesc);
 
+        void ClearDynamicGroup();
+
         template<typename T>
         T* FindContract()
         {

+ 13 - 11
Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.cpp

@@ -1036,12 +1036,12 @@ namespace ScriptCanvas
                     if (azrtti_istypeof<ScriptCanvas::Nodes::NodeableNodeOverloaded*>(&node))
                     {
                         // todo Add node to these errors
-                        AddError(nullptr, ValidationConstPtr(aznew NotYetImplemented(node.GetEntityId(), AZStd::string::format("NodeableNodeOverloaded doesn't have enough data connected to select a valid overload: %s", node.GetDebugName().data()))));
+                        AddError(nullptr, ValidationConstPtr(aznew Internal::ParseError(node.GetEntityId(), AZStd::string::format("NodeableNodeOverloaded doesn't have enough data connected to select a valid overload: %s", node.GetDebugName().data()))));
                     }
                     else
                     {
                         // todo Add node to these errors
-                        AddError(nullptr, ValidationConstPtr(aznew NotYetImplemented(node.GetEntityId(), AZStd::string::format("NodeableNode did not construct its internal node: %s", node.GetDebugName().data()))));
+                        AddError(nullptr, ValidationConstPtr(aznew Internal::ParseError(node.GetEntityId(), AZStd::string::format("NodeableNode did not construct its internal node: %s", node.GetDebugName().data()))));
                     }
                 }
             }
@@ -2535,7 +2535,7 @@ namespace ScriptCanvas
 
             if (IsInfiniteVariableWriteHandlingLoop(*this, variableHandling, variableHandling->m_function, true))
             {
-                AddError(variableHandling->m_function, aznew NotYetImplemented(AZ::EntityId(), ScriptCanvas::ParseErrors::InfiniteLoopWritingToVariable));
+                AddError(variableHandling->m_function, aznew Internal::ParseError(AZ::EntityId(), ScriptCanvas::ParseErrors::InfiniteLoopWritingToVariable));
                 return false;
             }
 
@@ -3312,7 +3312,7 @@ namespace ScriptCanvas
                     }
                     else
                     {
-                        AddError(execution, aznew NotYetImplemented(execution->GetNodeId(), childOutSlotsOutcome.TakeError()));
+                        AddError(execution, aznew Internal::ParseError(execution->GetNodeId(), childOutSlotsOutcome.TakeError()));
                     }
                 }
             }
@@ -3615,9 +3615,11 @@ namespace ScriptCanvas
 
         void AbstractCodeModel::ParseExecutionMultipleOutSyntaxSugar(ExecutionTreePtr execution, const EndpointsResolved& executionOutNodes, const AZStd::vector<const Slot*>& outSlots)
         {
+            const auto executionNodeId = execution->GetId().m_node ? execution->GetId().m_node->GetEntityId() : AZ::EntityId();
+
             if (executionOutNodes.size() != outSlots.size())
             {
-                AddError(AZ::EntityId(), execution, ParseErrors::ParseExecutionMultipleOutSyntaxSugarMismatchOutSize);
+                AddError(executionNodeId, execution, ParseErrors::ParseExecutionMultipleOutSyntaxSugarMismatchOutSize);
             }
 
             if (execution->GetSymbol() != Symbol::Sequence)
@@ -3630,13 +3632,13 @@ namespace ScriptCanvas
 
                 if (!child)
                 {
-                    AddError(AZ::EntityId(), execution, ParseErrors::ParseExecutionMultipleOutSyntaxSugarNullChildFound);
+                    AddError(executionNodeId, execution, ParseErrors::ParseExecutionMultipleOutSyntaxSugarNullChildFound);
                     return;
                 }
 
                 if (child->m_execution)
                 {
-                    AddError(AZ::EntityId(), execution, ParseErrors::ParseExecutionMultipleOutSyntaxSugarNonNullChildExecutionFound);
+                    AddError(executionNodeId, execution, ParseErrors::ParseExecutionMultipleOutSyntaxSugarNonNullChildExecutionFound);
                     return;
                 }
 
@@ -3660,7 +3662,7 @@ namespace ScriptCanvas
 
                 if (execution->GetChildrenCount() != executionOutNodes.size())
                 {
-                    AddError(AZ::EntityId(), execution, ParseErrors::ParseExecutionMultipleOutSyntaxSugarChildExecutionRemovedAndNotReplaced);
+                    AddError(executionNodeId, execution, ParseErrors::ParseExecutionMultipleOutSyntaxSugarChildExecutionRemovedAndNotReplaced);
                     return;
                 }
             }
@@ -4187,7 +4189,7 @@ namespace ScriptCanvas
                 }
                 else
                 {
-                    AddError(nullptr, aznew NotYetImplemented(execution->GetNodeId(), dataSlotsOutcome.TakeError()));
+                    AddError(nullptr, aznew Internal::ParseError(execution->GetNodeId(), dataSlotsOutcome.TakeError()));
                 }
             }
         }
@@ -4494,13 +4496,13 @@ namespace ScriptCanvas
                     }
                     else
                     {
-                        AddError(execution, aznew NotYetImplemented(execution->GetNodeId(), returnSlotsOutcome.TakeError()));
+                        AddError(execution, aznew Internal::ParseError(execution->GetNodeId(), returnSlotsOutcome.TakeError()));
                     }
                 }
             }
             else
             {
-                AddError(execution, aznew NotYetImplemented(execution->GetNodeId(), outputSlotsOutcome.TakeError()));
+                AddError(execution, aznew Internal::ParseError(execution->GetNodeId(), outputSlotsOutcome.TakeError()));
             }
         }
 

+ 0 - 9
Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/BinaryOperator.cpp

@@ -35,15 +35,6 @@ namespace ScriptCanvas
             }
         }
 
-        AZStd::unordered_map<AZStd::string, AZStd::vector<AZStd::string>> ArithmeticExpression::GetReplacementSlotsMap() const
-        {
-            AZStd::unordered_map<AZStd::string, AZStd::vector<AZStd::string>> slotsMap;
-            slotsMap.emplace(k_evaluateName, AZStd::vector<AZStd::string>{ "In" });
-            slotsMap.emplace(k_outName, AZStd::vector<AZStd::string>{ "Out" });
-            slotsMap.emplace(k_resultName, AZStd::vector<AZStd::string>{ "Result" });
-            return slotsMap;
-        }
-
         void ArithmeticExpression::OnInit()
         {
             {

+ 0 - 1
Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/BinaryOperator.h

@@ -72,7 +72,6 @@ namespace ScriptCanvas
 
             bool IsDeprecated() const override { return true; }
             
-            AZStd::unordered_map<AZStd::string, AZStd::vector<AZStd::string>> GetReplacementSlotsMap() const override;
             void CustomizeReplacementNode(Node* replacementNode, AZStd::unordered_map<SlotId, AZStd::vector<SlotId>>& outSlotIdMap) const override;
 
         protected:

+ 0 - 184
Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/ForEach.cpp

@@ -94,8 +94,6 @@ namespace ScriptCanvas
 
             void ForEach::OnInit()
             {
-                ResetLoop();
-
                 if (!m_sourceSlot.IsValid())
                 {
                     DynamicDataSlotConfiguration slotConfiguration;
@@ -130,33 +128,6 @@ namespace ScriptCanvas
                 EndpointNotificationBus::Handler::BusConnect({ GetEntityId(), m_sourceSlot });
             }
 
-            void ForEach::OnInputSignal(const SlotId& slotId)
-            {
-                auto inSlotId = ForEachProperty::GetInSlotId(this);
-                if (slotId == inSlotId || slotId == SlotId{})
-                {
-                    if (slotId == inSlotId)
-                    {
-                        if (!InitializeLoop())
-                        {
-                            // Loop initialization failed
-                            SignalOutput(ForEachProperty::GetFinishedSlotId(this));
-                            return;
-                        }
-                    }
-
-                    if (!m_breakCalled)
-                    {
-                        Iterate();
-                    }
-                }
-                else if (slotId == ForEachProperty::GetBreakSlotId(this))
-                {
-                    m_breakCalled = true;
-                    SignalOutput(ForEachProperty::GetFinishedSlotId(this));
-                }
-            }
-
             UpdateResult ForEach::OnUpdateNode()
             {
                 if (auto continueSlot = GetSlotByNameAndType("Continue", CombinedSlotType::ExecutionIn))
@@ -167,161 +138,6 @@ namespace ScriptCanvas
                 return UpdateResult::DirtyGraph;
             }
 
-            bool ForEach::InitializeLoop()
-            {
-                ResetLoop();
-
-                const Datum* input = FindDatum(m_sourceSlot);
-
-                if (input && !input->Empty())
-                {
-                    if (!Data::IsContainerType(input->GetType()))
-                    {
-                        SCRIPTCANVAS_REPORT_ERROR((*this), "Iteration not supported on this type: %s", Data::GetName(m_sourceContainer.GetType()).c_str());
-                        return false;
-                    }
-
-                    // Make a copy of the source datum
-                    m_sourceContainer = *input;
-
-                    // Get the size of the container
-                    auto sizeOutcome = BehaviorContextMethodHelper::CallMethodOnDatum(m_sourceContainer, "Size");
-
-                    if (!sizeOutcome)
-                    {
-                        SCRIPTCANVAS_REPORT_ERROR((*this), "Failed to get size of container: %s", sizeOutcome.GetError().c_str());
-                        return false;
-                    }
-
-                    Datum sizeResult = sizeOutcome.TakeValue();
-                    const size_t* sizePtr = sizeResult.GetAs<size_t>();
-                    m_size = sizePtr ? *sizePtr : 0;
-
-                    if (Data::IsSetContainerType(m_sourceContainer.GetType()) || Data::IsMapContainerType(m_sourceContainer.GetType()))
-                    {
-                        // If it's a map or set, get the vector of keys
-                        auto keysVectorOutcome = BehaviorContextMethodHelper::CallMethodOnDatum(m_sourceContainer, "GetKeys");
-
-                        if (!keysVectorOutcome)
-                        {
-                            SCRIPTCANVAS_REPORT_ERROR((*this), "Failed to get vector of keys: %s", keysVectorOutcome.GetError().c_str());
-                            return false;
-                        }
-
-                        m_keysVector = keysVectorOutcome.TakeValue();
-
-                        // Check size of vector of keys for safety
-                        auto keysSizeOutcome = BehaviorContextMethodHelper::CallMethodOnDatum(m_keysVector, "Size");
-
-                        if (!keysSizeOutcome)
-                        {
-                            SCRIPTCANVAS_REPORT_ERROR((*this), "Failed to get size of vector of keys: %s", keysSizeOutcome.GetError().c_str());
-                            return false;
-                        }
-
-                        Datum keysSizeResult = keysSizeOutcome.TakeValue();
-                        const size_t* keysSizePtr = keysSizeResult.GetAs<size_t>();
-                        size_t keysSize = keysSizePtr ? *keysSizePtr : 0;
-
-                        if (m_size != keysSize)
-                        {
-                            // This shouldn't happen
-                            SCRIPTCANVAS_REPORT_ERROR((*this), "Container size and vector of keys size mismatch.");
-                            return false;
-                        }
-                    }
-
-                    return true;
-                }
-
-                return false;
-            }
-
-            void ForEach::Iterate()
-            {
-                if (m_sourceContainer.Empty() || m_index >= m_size)
-                {
-                    SignalOutput(ForEachProperty::GetFinishedSlotId(this));
-                    return;
-                }
-
-                Datum& container = Data::IsVectorContainerType(m_sourceContainer.GetType()) ? m_sourceContainer : m_keysVector;
-
-                auto keyAtOutcome = BehaviorContextMethodHelper::CallMethodOnDatumUnpackOutcomeSuccess(container, "At", m_index);
-
-                if (!keyAtOutcome)
-                {
-                    SCRIPTCANVAS_REPORT_ERROR((*this), "Failed to get key in container: %s", keyAtOutcome.GetError().c_str());
-                    return;
-                }
-
-                Datum keyAtResult = keyAtOutcome.TakeValue();
-
-                if (!SetPropertySlotData(keyAtResult, k_keySlotIndex))
-                {
-                    // Unable to set property slot
-                    SCRIPTCANVAS_REPORT_ERROR((*this), "Unable to set one of the property slots on this node.");
-                    SignalOutput(ForEachProperty::GetFinishedSlotId(this));
-                    return;
-                }
-
-                if (Data::IsMapContainerType(m_sourceContainer.GetType()))
-                {
-                    // If the container is a map, we want to get the value for the current key
-                    auto valueAtOutcome = BehaviorContextMethodHelper::CallMethodOnDatumUnpackOutcomeSuccess(m_sourceContainer, "At", keyAtResult);
-
-                    if (!valueAtOutcome)
-                    {
-                        SCRIPTCANVAS_REPORT_ERROR((*this), "Failed to get value for key in container: %s", valueAtOutcome.GetError().c_str());
-                        return;
-                    }
-
-                    Datum valueAtResult = valueAtOutcome.TakeValue();
-
-                    if (!SetPropertySlotData(valueAtResult, k_valueSlotIndex))
-                    {
-                        // Unable to set property slot
-                        SCRIPTCANVAS_REPORT_ERROR((*this), "Unable to set one of the property slots on this node.");
-                        SignalOutput(ForEachProperty::GetFinishedSlotId(this));
-                        return;
-                    }
-                }
-
-                ++m_index;
-
-                SignalOutput(ForEachProperty::GetEachSlotId(this));
-            }
-
-            bool ForEach::SetPropertySlotData(Datum& atResult, size_t propertyIndex)
-            {
-                if (atResult.Empty())
-                {
-                    // Something went wrong with the Behavior Context call
-                    SCRIPTCANVAS_REPORT_ERROR((*this), "Behavior Context call failed; unable to retrieve element from container.");
-                    return false;
-                }
-
-                if (m_propertySlots.size() <= propertyIndex)
-                {
-                    // Missing a property slot
-                    SCRIPTCANVAS_REPORT_ERROR((*this), "Node in invalid state; missing a property slot.");
-                    return false;
-                }
-
-                PushOutput(atResult, *GetSlot(m_propertySlots[propertyIndex].m_propertySlotId));
-                return true;
-            }
-
-            void ForEach::ResetLoop()
-            {
-                // Reset node state
-                m_index = 0;
-                m_size = 0;
-                m_breakCalled = false;
-                m_sourceContainer = Datum();
-                m_keysVector = Datum();
-            }
-
             void ForEach::OnDynamicGroupDisplayTypeChanged(const AZ::Crc32& dynamicGroup, const Data::Type& dataType)
             {
                 if (dynamicGroup == GetContainerGroupId() && dataType.IsValid())

+ 11 - 26
Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/ForEach.h

@@ -56,44 +56,29 @@ namespace ScriptCanvas
 
                 bool IsBreakSlot(const SlotId&) const;
 
-                bool IsOutOfDate(const VersionData& graphVersion) const override;
-
-                
+                bool IsOutOfDate(const VersionData& graphVersion) const override;              
 
                 UpdateResult OnUpdateNode() override;
 
-            protected:
-                ExecutionNameMap GetExecutionNameMap() const override;
-
-                void OnInit() override;
-                void OnInputSignal(const SlotId&) override;
+            private:
+                static const size_t k_keySlotIndex;
+                static const size_t k_valueSlotIndex;
 
-                bool InitializeLoop();
-                void Iterate();
-                bool SetPropertySlotData(Datum& atResult, size_t propertyIndex);
-                void ResetLoop();
+                static AZ::Crc32 GetContainerGroupId() { return AZ_CRC("ContainerGroup", 0xb81ed451); }
 
-                void OnDynamicGroupDisplayTypeChanged(const AZ::Crc32& dynamicGroup, const Data::Type& dataType) override;
+                void AddPropertySlotsFromType(const Data::Type& dataType);
 
                 void ClearPropertySlots();
-                void AddPropertySlotsFromType(const Data::Type& dataType);
 
-                static AZ::Crc32 GetContainerGroupId() { return AZ_CRC("ContainerGroup", 0xb81ed451); }
+                ExecutionNameMap GetExecutionNameMap() const override;
+
+                void OnInit() override;
 
+                void OnDynamicGroupDisplayTypeChanged(const AZ::Crc32& dynamicGroup, const Data::Type& dataType) override;
+                                                
                 SlotId m_sourceSlot;
                 AZ::TypeId m_previousTypeId;
                 AZStd::vector<Data::PropertyMetadata> m_propertySlots;
-
-                static const size_t k_keySlotIndex;
-                static const size_t k_valueSlotIndex;
-
-                size_t m_index;
-                size_t m_size;
-
-                bool m_breakCalled;
-
-                Datum m_sourceContainer;
-                Datum m_keysVector;
             };
         }
     }

+ 5 - 0
Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/FunctionCallNode.cpp

@@ -410,6 +410,11 @@ namespace ScriptCanvas
                 return m_asset.GetId();
             }
 
+            const AZStd::string& FunctionCallNode::GetAssetHint() const
+            {
+                return m_asset.GetHint();
+            }
+
             AZ::Outcome<DependencyReport, void> FunctionCallNode::GetDependencies() const
             {
                 DependencyReport report;

+ 2 - 0
Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/FunctionCallNode.h

@@ -70,6 +70,8 @@ namespace ScriptCanvas
 
                 AZ::Data::AssetId GetAssetId() const;
 
+                const AZStd::string& GetAssetHint() const;
+
                 const AZStd::string& GetName() const;
 
                 void Initialize(AZ::Data::AssetId assetId, const ScriptCanvas::Grammar::FunctionSourceId& sourceId);

+ 22 - 1
Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/FunctionDefinitionNode.cpp

@@ -113,6 +113,28 @@ namespace ScriptCanvas
                 }
             }
 
+            void FunctionDefinitionNode::OnInit()
+            {
+                Nodeling::OnInit();
+
+                AZ::SerializeContext* serializeContext{};
+                AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
+                if (serializeContext)
+                {
+                    const auto& classData = serializeContext->FindClassData(azrtti_typeid<FunctionDefinitionNode>());
+                    if (classData && classData->m_version < NodeVersion::RemoveDefaultDisplayGroup)
+                    {
+                        for (auto& slot : ModAllSlots())
+                        {
+                            if (slot->GetType() == CombinedSlotType::DataIn || slot->GetType() == CombinedSlotType::DataOut)
+                            {
+                                slot->ClearDynamicGroup();
+                            }
+                        }
+                    }
+                }
+            }
+
             void FunctionDefinitionNode::SetupSlots()
             {
                 auto groupedSlots = GetSlotsWithDisplayGroup(GetSlotDisplayGroup());
@@ -208,7 +230,6 @@ namespace ScriptCanvas
                 slotConfiguration.SetConnectionType(connectionType);
 
                 slotConfiguration.m_displayGroup = GetDataDisplayGroup();
-                slotConfiguration.m_dynamicGroup = GetDataDynamicTypeGroup();
                 slotConfiguration.m_dynamicDataType = DynamicDataType::Any;
                 slotConfiguration.m_isUserAdded = true;
 

+ 5 - 3
Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/FunctionDefinitionNode.h

@@ -32,7 +32,8 @@ namespace ScriptCanvas
             private:
                 enum NodeVersion
                 {
-                    Initial  = 1
+                    Initial  = 1,
+                    RemoveDefaultDisplayGroup,
                 };
 
             public:
@@ -78,14 +79,15 @@ namespace ScriptCanvas
 
                 static constexpr AZ::Crc32 GetAddNodelingInputDataSlot() { return AZ_CRC_CE("AddNodelingInputDataSlot"); }
                 static constexpr AZ::Crc32 GetAddNodelingOutputDataSlot() { return AZ_CRC_CE("AddNodelingOutputDataSlot"); }
-                static constexpr AZ::Crc32 GetDataDynamicTypeGroup() { return AZ_CRC_CE("DataGroup"); }
-
+                
                 AZStd::string GetDataDisplayGroup() const { return "DataDisplayGroup"; }
                 
                 SlotId HandleExtension(AZ::Crc32 extensionId) override;
 
                 void ConfigureVisualExtensions() override;
 
+                void OnInit() override;
+
                 void OnSetup() override;
 
             private:

+ 5 - 2
Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.cpp

@@ -208,7 +208,6 @@ namespace ScriptCanvas
                 }
             }
 
-
             void Method::InitializeMethod(const MethodConfiguration& config)
             {
                 m_namespaces = config.m_namespaces ? *config.m_namespaces : m_namespaces;
@@ -239,7 +238,11 @@ namespace ScriptCanvas
                 for (size_t argIndex(0), sentinel(config.m_method.GetNumArguments()); argIndex != sentinel; ++argIndex)
                 {
                     SlotId addedSlot = AddMethodInputSlot(config, argIndex);
-                    MethodHelper::SetSlotToDefaultValue(*this, addedSlot, config, argIndex);
+
+                    if (addedSlot.IsValid())
+                    {
+                        MethodHelper::SetSlotToDefaultValue(*this, addedSlot, config, argIndex);
+                    }
                 }
             }
 

+ 4 - 0
Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.h

@@ -132,6 +132,8 @@ namespace ScriptCanvas
 
                 const Slot* GetIfBranchSlot(bool branch) const;
 
+                AZ_INLINE const AZStd::string& GetLookupName() const { return m_lookupName; }
+
                 AZ_INLINE AZStd::recursive_mutex& GetMutex() { return m_mutex; }
 
                 ConstSlotsOutcome GetSlotsInExecutionThreadByTypeImpl(const Slot& executionSlot, CombinedSlotType targetSlotType, const Slot* executionChildSlot) const override;
@@ -160,6 +162,8 @@ namespace ScriptCanvas
 
                 bool SanityCheckBranchOnResultMethod(const AZ::BehaviorMethod& branchOnResultMethod) const;
 
+                AZ_INLINE void SetClassNamePretty(AZStd::string_view classNamePretty) { m_classNamePretty = classNamePretty; }
+
                 void SetMethodUnchecked(const AZ::BehaviorMethod* method, const AZ::BehaviorClass* behaviorClass);
 
                 AZ_INLINE void SetWarnOnMissingFunction(bool enabled) { m_warnOnMissingFunction = enabled; }

+ 39 - 10
Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/MethodOverloaded.cpp

@@ -94,7 +94,7 @@ namespace ScriptCanvas
             {
                 if (!m_updatingDisplay)
                 {
-                    RefreshActiveIndexes();
+                    RefreshActiveIndexes(true, true);
                     UpdateSlotDisplay();
                 }
             }
@@ -135,10 +135,8 @@ namespace ScriptCanvas
                 return Data::Type::Invalid();
             }
 
-            AZ::Outcome<AZStd::string, void> MethodOverloaded::GetFunctionCallName(const Slot* slot) const
+            AZ::Outcome<AZStd::string, void> MethodOverloaded::GetFunctionCallName([[maybe_unused]] const Slot* slot) const
             {
-                AZ_UNUSED(slot);
-
                 AZStd::string overloadName;
 
                 int activeIndex = GetActiveIndex();
@@ -188,7 +186,7 @@ namespace ScriptCanvas
 
                 // this prevents repeated updates based on changes to slots
                 Method::InitializeMethod(config);
-
+                SetClassNamePretty("");
                 RefreshActiveIndexes();
 
                 ConfigureContracts();
@@ -197,7 +195,12 @@ namespace ScriptCanvas
             SlotId MethodOverloaded::AddMethodInputSlot(const MethodConfiguration& config, size_t argumentIndex)
             {
                 const AZ::BehaviorParameter* argumentPtr = config.m_method.GetArgument(argumentIndex);
-                AZ_Assert(argumentPtr, "Method: %s had a null argument at index: %d", config.m_lookupName->data(), argumentIndex);
+
+                if (!argumentPtr)
+                {
+                    return SlotId{};
+                }
+
                 const auto& argument = *argumentPtr;
                 auto nameAndToolTip = MethodHelper::GetArgumentNameAndToolTip(config, argumentIndex);
 
@@ -507,7 +510,7 @@ namespace ScriptCanvas
                 }
             }
 
-            void MethodOverloaded::RefreshActiveIndexes(bool checkForConnections)
+            void MethodOverloaded::RefreshActiveIndexes(bool checkForConnections, bool adjustSlots)
             {
                 DataIndexMapping concreteInputTypes;
                 DataIndexMapping concreteOutputTypes;
@@ -519,6 +522,35 @@ namespace ScriptCanvas
                 if (m_overloadSelection.m_availableIndexes.size() == 1)
                 {
                     auto methodOverload = m_overloadConfiguration.m_overloads[(*m_overloadSelection.m_availableIndexes.begin())];
+
+                    if (adjustSlots)
+                    {
+                        const size_t numArguments = methodOverload.first->GetNumArguments();
+                        const size_t numInputSlots = m_orderedInputSlotIds.size();
+
+                        if (numArguments > numInputSlots)
+                        {
+                            MethodConfiguration config(*methodOverload.first, GetMethodType());
+                            AZStd::string_view lookupName = GetLookupName();
+                            config.m_lookupName = &lookupName;
+
+                            for (size_t index = numInputSlots; index != numArguments; ++index)
+                            {
+                                AddMethodInputSlot(config, index);
+                            }
+                        }
+                        else if (numArguments < numInputSlots)
+                        {
+                            const size_t removeCount = numInputSlots - numArguments;
+                            // remove extra slots, assuming remaining ones are of valid type (if not valid name)
+                            for (size_t count = 0; count != removeCount; ++count)
+                            {
+                                RemoveSlot(m_orderedInputSlotIds.back());
+                                m_orderedInputSlotIds.pop_back();
+                            }
+                        }
+                    }
+
                     SetMethodUnchecked(methodOverload.first, methodOverload.second);
                 }
             }
@@ -681,9 +713,6 @@ namespace ScriptCanvas
 
                 return AZ::Success();
             }
-
         } 
-
     } 
-
 } 

+ 1 - 1
Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/MethodOverloaded.h

@@ -106,7 +106,7 @@ namespace ScriptCanvas
                 void SetupMethodData(const AZ::BehaviorMethod* lookupMethod, const AZ::BehaviorClass* lookupClass);
                 void ConfigureContracts();
 
-                void RefreshActiveIndexes(bool checkForConnections = true);
+                void RefreshActiveIndexes(bool checkForConnections = true, bool adjustSlots = false);
                 void FindDataIndexMappings(DataIndexMapping& inputMapping, DataIndexMapping& outputMapping, bool checkForConnections) const;
 
                 void UpdateSlotDisplay();

+ 0 - 8
Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Operators/Operator.cpp

@@ -393,14 +393,6 @@ namespace ScriptCanvas
                 AZ_UNUSED(sourceType);
             }
 
-            AZStd::unordered_map<AZStd::string, AZStd::vector<AZStd::string>> OperatorBase::GetReplacementSlotsMap() const
-            {
-                AZStd::unordered_map<AZStd::string, AZStd::vector<AZStd::string>> slotsMap;
-                slotsMap.emplace("In", AZStd::vector<AZStd::string>{ "In" });
-                slotsMap.emplace("Out", AZStd::vector<AZStd::string>{ "Out" });
-                return slotsMap;
-            }
-
             void OperatorBase::CustomizeReplacementNode(Node* replacementNode, AZStd::unordered_map<SlotId, AZStd::vector<SlotId>>& outSlotIdMap) const
             {
                 auto newDataInSlots = replacementNode->GetSlotsByType(ScriptCanvas::CombinedSlotType::DataIn);

+ 0 - 1
Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Operators/Operator.h

@@ -57,7 +57,6 @@ namespace ScriptCanvas
                     AZStd::vector< SourceSlotConfiguration > m_sourceSlotConfigurations;
                 };
 
-                AZStd::unordered_map<AZStd::string, AZStd::vector<AZStd::string>> GetReplacementSlotsMap() const override;
                 void CustomizeReplacementNode(Node* replacementNode, AZStd::unordered_map<SlotId, AZStd::vector<SlotId>>& outSlotIdMap) const override;
 
                 using TypeList = AZStd::vector<AZ::TypeId>;

+ 2 - 2
Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Time/Timer.ScriptCanvasGrammar.xml

@@ -8,7 +8,7 @@
            Base="ScriptCanvas::Node"
            Version="2"
            GeneratePropertyFriend="True"
-           DeprecationUUID="32A4BEDC-C207-4472-61DE-9A716402620A"
+           DeprecationUUID="{32A4BEDC-C207-4472-61DE-9A716402620A}"
            Deprecated="This node has been deprecated in favor of the nodeable form"
            Description="Provides a time value.">
         <In Name="Start" Description="Starts the timer."/>
@@ -25,4 +25,4 @@
                   IsInput="False"
                   IsOutput="True" />
     </Class>
-</ScriptCanvas>
+</ScriptCanvas>

+ 110 - 0
Gems/ScriptCanvas/Code/Include/ScriptCanvas/Utils/VersioningUtils.cpp

@@ -12,9 +12,26 @@
 #include "VersioningUtils.h"
 
 #include <ScriptCanvas/Core/Graph.h>
+#include <ScriptCanvas/Core/Connection.h>
 
 namespace ScriptCanvas
 {
+    AZStd::vector<Endpoint> GraphUpdateSlotReport::Convert(const Endpoint& oldEndpoint) const
+    {
+        auto iter = m_oldSlotsToNewSlots.find(oldEndpoint);
+        return iter != m_oldSlotsToNewSlots.end() ? iter->second : AZStd::vector<Endpoint>{ oldEndpoint };
+    }
+
+    bool GraphUpdateSlotReport::IsEmpty() const
+    {
+        return m_deletedOldSlots.empty() && m_oldSlotsToNewSlots.empty();
+    }
+
+    bool NodeUpdateSlotReport::IsEmpty() const
+    {
+        return m_deletedOldSlots.empty() && m_oldSlotsToNewSlots.empty();
+    }
+
     void VersioningUtils::CopyOldValueToDataSlot(Slot* newSlot, const VariableId& oldVariableReference, const Datum* oldDatum)
     {
         if (oldVariableReference.IsValid())
@@ -36,6 +53,99 @@ namespace ScriptCanvas
         }
     }
 
+    void MergeUpdateSlotReport(const AZ::EntityId& scriptCanvasNodeId, GraphUpdateSlotReport& report, const NodeUpdateSlotReport& source)
+    {
+        report.m_deletedOldSlots.reserve(source.m_deletedOldSlots.size());
+
+        for (auto& slotId : source.m_deletedOldSlots)
+        {
+            report.m_deletedOldSlots.insert({ scriptCanvasNodeId, slotId });
+        }
+
+        report.m_oldSlotsToNewSlots.reserve(source.m_oldSlotsToNewSlots.size());
+
+        for (auto& oldToNewIter : source.m_oldSlotsToNewSlots)
+        {
+            AZStd::vector<Endpoint> newEndpoints;
+            newEndpoints.reserve(oldToNewIter.second.size());
+
+            for (auto& targetSlotId : oldToNewIter.second)
+            {
+                newEndpoints.push_back({ scriptCanvasNodeId, targetSlotId });
+            }
+
+            report.m_oldSlotsToNewSlots[{ scriptCanvasNodeId, oldToNewIter.first}] = AZStd::move(newEndpoints);
+        }
+    }
+
+    AZStd::vector<AZStd::pair<Endpoint, Endpoint>> CollectEndpoints(const AZStd::vector<AZ::Entity*>& connections, bool logEntityNames)
+    {
+        AZStd::vector<AZStd::string> names;
+        AZStd::vector<AZStd::pair<Endpoint, Endpoint>> endpoints;
+
+        for (auto& connectionEntity : connections)
+        {
+            if (logEntityNames)
+            {
+                names.push_back(connectionEntity->GetName());
+            }
+
+            if (auto connection = AZ::EntityUtils::FindFirstDerivedComponent<ScriptCanvas::Connection>(connectionEntity->GetId()))
+            {
+                endpoints.push_back(AZStd::make_pair(connection->GetSourceEndpoint(), connection->GetTargetEndpoint()));
+            }
+        }
+
+        if (logEntityNames)
+        {
+            AZStd::sort(names.begin(), names.end());
+
+            AZStd::string result = "\nConnection Name list:\n";
+            for (auto& name : names)
+            {
+                result += "\n";
+                result += name;
+            }
+
+            AZ_TracePrintf("ScriptCanvas", result.c_str());
+        }
+
+        return endpoints;
+    }
+
+    void UpdateConnectionStatus(Graph& graph, const GraphUpdateSlotReport& report)
+    {
+         GraphData* graphData = graph.GetGraphData();
+         if (!graphData)
+         {
+             AZ_Error("ScriptCanvas", false, "Graph was missing graph data to update");
+             return;
+         }
+ 
+         AZStd::unordered_set<SlotId> oldConnectedSlots;
+         AZ_TracePrintf("ScriptCanvas", "Connections list before: ");
+         auto endpoints = CollectEndpoints(graphData->m_connections, true);
+         graph.RemoveAllConnections();
+
+         for (auto& iter : endpoints)
+         {
+             const AZStd::vector<Endpoint>& sources = report.Convert(iter.first);
+             const AZStd::vector<Endpoint>& targets = report.Convert(iter.second);
+
+             for (const auto& source : sources)
+             {
+                 for (const auto& target : targets)
+                 {
+                     graph.ConnectByEndpoint(source, target);
+                 }
+             }
+         }
+
+         graphData->BuildEndpointMap();
+         AZ_TracePrintf("ScriptCanvas", "Connections list after: ");
+         CollectEndpoints(graphData->m_connections, true);
+    }
+
     void VersioningUtils::CreateRemapConnectionsForSourceEndpoint(const Graph& graph, const Endpoint& oldSourceEndpoint, const Endpoint& newSourceEndpoint,
         ReplacementConnectionMap& connectionMap)
     {

+ 27 - 2
Gems/ScriptCanvas/Code/Include/ScriptCanvas/Utils/VersioningUtils.h

@@ -15,6 +15,8 @@
 #include <AzCore/std/containers/unordered_set.h>
 #include <AzCore/std/utils.h>
 
+#include <ScriptCanvas/Core/Core.h>
+#include <ScriptCanvas/Core/Endpoint.h>
 #include <ScriptCanvas/Variable/VariableCore.h>
 
 namespace AZ
@@ -25,13 +27,36 @@ namespace AZ
 namespace ScriptCanvas
 {
     class Datum;
-    class Endpoint;
     class Graph;
     class Slot;
 
-    using ReplacementEndpointPairs = AZStd::unordered_set<AZStd::pair<ScriptCanvas::Endpoint, ScriptCanvas::Endpoint>>;
+    using ReplacementEndpointPairs = AZStd::unordered_set<AZStd::pair<Endpoint, Endpoint>>;
     using ReplacementConnectionMap = AZStd::unordered_map<AZ::EntityId, ReplacementEndpointPairs>;
 
+    struct NodeUpdateSlotReport
+    {
+        AZStd::unordered_set<SlotId> m_deletedOldSlots;
+        AZStd::unordered_map<SlotId, AZStd::vector<SlotId>> m_oldSlotsToNewSlots;
+
+        bool IsEmpty() const;
+    };
+
+    struct GraphUpdateSlotReport
+    {
+        AZStd::unordered_set<Endpoint> m_deletedOldSlots;
+        AZStd::unordered_map<Endpoint, AZStd::vector<Endpoint>> m_oldSlotsToNewSlots;
+
+        AZStd::vector<Endpoint> Convert(const Endpoint& oldEndpoint) const;
+
+        bool IsEmpty() const;
+    };
+
+    void MergeUpdateSlotReport(const AZ::EntityId& scriptCanvasNodeId, GraphUpdateSlotReport& report, const NodeUpdateSlotReport& source);
+
+    AZStd::vector<AZStd::pair<Endpoint, Endpoint>> CollectEndpoints(const AZStd::vector<AZ::Entity*>& connections, bool logEntityNames = false);
+
+    void UpdateConnectionStatus(Graph& graph, const GraphUpdateSlotReport& report);
+
     class VersioningUtils
     {
     public:

+ 4 - 2
Gems/StartingPointInput/Code/Source/InputNode.ScriptCanvasGrammar.xml

@@ -6,8 +6,10 @@
 	       PreferredClassName="Input Handler"
            Uuid="{0B0AC61B-4BBA-42BF-BDCD-DAF2D3CA41A8}"
            Base="ScriptCanvas::Node"
-           Icon="Icons/ScriptCanvas/Bus.png"
+           Icon="Editor/Icons/ScriptCanvas/Bus.png"
            EditAttributes="AZ::Edit::Attributes::Category@Gameplay/Input"
+           DeprecationUUID="{0A2EB488-5A6A-E166-BB62-23FF81499E33}"
+           Deprecated="This node has been deprecated in favor of the nodeable form"
            GraphEntryPoint="True"
            GeneratePropertyFriend="True"
            Description="Handle processed input events found in input binding assets">
@@ -25,4 +27,4 @@
                   IsInput="False"
                   IsOutput="True" />
     </Class>
-</ScriptCanvas>
+</ScriptCanvas>