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

SC support for unordered_set, version upgrade improvements and bug fixes
This commit is contained in:
carlitosan
2021-05-25 17:39:06 -07:00
committed by GitHub
38 changed files with 518 additions and 415 deletions
@@ -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 <>
@@ -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,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));
@@ -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;
};
@@ -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;
@@ -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;
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,27 +3694,13 @@ namespace ScriptCanvasEditor
}
}
// Remap connections step should be done before editor processing connections for graph
//
// Delete underlying data conenections.
for (auto remapConnection : remapConnections)
if (!graphUpdateSlotReport.IsEmpty())
{
RemoveConnection(remapConnection.first);
// 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);
}
// Recreate connections in a separate pass to avoid triggering display updates for invalid slot ids.
for (auto remapConnection : remapConnections)
{
for (auto newEndpointPair : remapConnection.second)
{
if (newEndpointPair.first.IsValid() && newEndpointPair.second.IsValid())
{
ConnectByEndpoint(newEndpointPair.first, newEndpointPair.second);
}
}
}
////
AZStd::unordered_set<AZ::EntityId> graphCanvasNodesToDelete;
for (auto scriptCanvasNode : outOfDateNodes)
@@ -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()))
@@ -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;
@@ -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;
@@ -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;
@@ -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 %}
@@ -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)
@@ -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;
}
}
}
@@ -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;
@@ -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;
@@ -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 {}
@@ -347,6 +347,11 @@ namespace ScriptCanvas
}
}
void Slot::ClearDynamicGroup()
{
m_dynamicGroup = AZ::Crc32{};
}
void Slot::ConvertToLatentExecutionOut()
{
if (IsExecution() && IsOutput())
@@ -68,6 +68,8 @@ namespace ScriptCanvas
void AddContract(const ContractDescriptor& contractDesc);
void ClearDynamicGroup();
template<typename T>
T* FindContract()
{
@@ -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("%s: %s", ParseErrors::NodeableNodeOverloadAmbiguous, 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("%s: %s", ParseErrors::NodeableNodeDidNotConstructInternalNodeable, 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()));
}
}
@@ -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()
{
{
@@ -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:
@@ -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())
@@ -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;
bool InitializeLoop();
void Iterate();
bool SetPropertySlotData(Datum& atResult, size_t propertyIndex);
void ResetLoop();
void OnDynamicGroupDisplayTypeChanged(const AZ::Crc32& dynamicGroup, const Data::Type& dataType) override;
void ClearPropertySlots();
void AddPropertySlotsFromType(const Data::Type& dataType);
static AZ::Crc32 GetContainerGroupId() { return AZ_CRC("ContainerGroup", 0xb81ed451); }
SlotId m_sourceSlot;
AZ::TypeId m_previousTypeId;
AZStd::vector<Data::PropertyMetadata> m_propertySlots;
private:
static const size_t k_keySlotIndex;
static const size_t k_valueSlotIndex;
size_t m_index;
size_t m_size;
static AZ::Crc32 GetContainerGroupId() { return AZ_CRC("ContainerGroup", 0xb81ed451); }
bool m_breakCalled;
void AddPropertySlotsFromType(const Data::Type& dataType);
Datum m_sourceContainer;
Datum m_keysVector;
void ClearPropertySlots();
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;
};
}
}
@@ -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;
@@ -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);
@@ -19,6 +19,32 @@
#include <ScriptCanvas/Debugger/ValidationEvents/DataValidation/InvalidPropertyEvent.h>
namespace FunctionDefinitionNodeCpp
{
void VersionUpdateRemoveDefaultDisplayGroup(ScriptCanvas::Nodes::Core::FunctionDefinitionNode& node)
{
using namespace ScriptCanvas;
using namespace ScriptCanvas::Nodes::Core;
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 < FunctionDefinitionNode::NodeVersion::RemoveDefaultDisplayGroup)
{
for (auto& slot : node.ModAllSlots())
{
if (slot->GetType() == CombinedSlotType::DataIn || slot->GetType() == CombinedSlotType::DataOut)
{
slot->ClearDynamicGroup();
}
}
}
}
}
}
namespace ScriptCanvas
{
namespace Nodes
@@ -113,6 +139,12 @@ namespace ScriptCanvas
}
}
void FunctionDefinitionNode::OnInit()
{
Nodeling::OnInit();
FunctionDefinitionNodeCpp::VersionUpdateRemoveDefaultDisplayGroup(*this);
}
void FunctionDefinitionNode::SetupSlots()
{
auto groupedSlots = GetSlotsWithDisplayGroup(GetSlotDisplayGroup());
@@ -208,7 +240,6 @@ namespace ScriptCanvas
slotConfiguration.SetConnectionType(connectionType);
slotConfiguration.m_displayGroup = GetDataDisplayGroup();
slotConfiguration.m_dynamicGroup = GetDataDynamicTypeGroup();
slotConfiguration.m_dynamicDataType = DynamicDataType::Any;
slotConfiguration.m_isUserAdded = true;
@@ -29,14 +29,13 @@ namespace ScriptCanvas
class FunctionDefinitionNode
: public Internal::Nodeling
{
private:
public:
enum NodeVersion
{
Initial = 1
Initial = 1,
RemoveDefaultDisplayGroup,
};
public:
SCRIPTCANVAS_NODE(FunctionDefinitionNode);
FunctionDefinitionNode() = default;
@@ -78,14 +77,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:
@@ -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);
}
}
}
@@ -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; }
@@ -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();
}
}
}
}
@@ -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();
@@ -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);
@@ -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>;
@@ -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>
@@ -62,6 +62,8 @@ namespace ScriptCanvas
constexpr const char* NoChildrenAfterRoot = "No children after parsing function root";
constexpr const char* NoChildrenInExtraction = "No children found in property extraction node";
constexpr const char* NoDataPresent = "Could not construct from graph, no graph data was present";
constexpr const char* NodeableNodeOverloadAmbiguous = "NodeableNodeOverloaded doesn't have enough data connected to select a valid overload";
constexpr const char* NodeableNodeDidNotConstructInternalNodeable = "NodeableNode did not construct its internal Nodeable";
constexpr const char* NoInputToForEach = "No Input To For Each Loop";
constexpr const char* NoOutForExecution = "No out slot for execution root";
constexpr const char* NoOutSlotInFunctionDefinitionStart = "No 'Out' slot in start of function definition";
@@ -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)
{
@@ -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:
@@ -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>