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
@@ -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: