You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/ScriptCanvas/Code/Editor/Components/GraphUpgrade.cpp

773 lines
28 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Editor/Include/ScriptCanvas/Components/GraphUpgrade.h>
#include <Editor/Include/ScriptCanvas/Components/EditorGraph.h>
#include <Editor/Nodes/NodeDisplayUtils.h>
#include <ScriptCanvas/Bus/RequestBus.h>
#include <GraphCanvas/Components/SceneBus.h>
#include <GraphCanvas/Components/EntitySaveDataBus.h>
#include <ScriptCanvas/Core/ConnectionBus.h>
#include <ScriptCanvas/GraphCanvas/MappingBus.h>
#include <ScriptCanvas/Bus/EditorScriptCanvasBus.h>
#include <GraphCanvas/GraphCanvasBus.h>
#include <Editor/GraphCanvas/GraphCanvasEditorNotificationBusId.h>
namespace ScriptCanvasEditor
{
namespace Helpers
{
static AZStd::string ConnectionToText(ScriptCanvasEditor::EditorGraph* graph, ScriptCanvas::Endpoint& from, ScriptCanvas::Endpoint& to)
{
AZ_Assert(graph, "A valid graph must be provided");
ScriptCanvas::Node* fromNode = graph->FindNode(from.GetNodeId());
ScriptCanvas::Node* toNode = graph->FindNode(to.GetNodeId());
ScriptCanvas::Slot* fromSlot = fromNode ? fromNode->GetSlot(from.GetSlotId()) : nullptr;
ScriptCanvas::Slot* toSlot = toNode ? toNode->GetSlot(to.GetSlotId()) : nullptr;
AZStd::string fromNodeName = fromNode ? fromNode->GetNodeName() : "Unknown Node";
AZStd::string toNodeName = toNode ? toNode->GetNodeName() : "Unknown Node";
AZStd::string fromSlotName = fromSlot ? fromSlot->GetName() : "Unknown Slot";
AZStd::string toSlotName = toSlot ? toSlot->GetName() : "Unknown Slot";
return AZStd::string::format("%s:%s to %s:%s", fromNodeName.c_str(), fromSlotName.c_str(), toNodeName.c_str(), toSlotName.c_str());
}
}
void Start::OnEnter()
{
EditorGraphUpgradeMachine* sm = GetStateMachine<EditorGraphUpgradeMachine>();
auto* graph = sm->m_graph;
GraphCanvas::GraphCanvasRequestBus::BroadcastResult(graph->m_graphCanvasSceneEntity, &GraphCanvas::GraphCanvasRequests::CreateSceneAndActivate);
if (!graph->m_graphCanvasSceneEntity)
{
// TODO: Need to shut it all down.
}
GraphCanvas::SceneRequestBus::Event(sm->m_graphCanvasGraphId, &GraphCanvas::SceneRequests::SetEditorId, ScriptCanvasEditor::AssetEditorId);
}
void PreventUndo::OnEnter()
{
GeneralRequestBus::Broadcast(&GeneralRequests::PushPreventUndoStateUpdate);
}
void CollectData::Run()
{
EditorGraphUpgradeMachine* sm = GetStateMachine<EditorGraphUpgradeMachine>();
auto* graph = sm->m_graph;
sm->m_scriptCanvasId = graph->GetScriptCanvasId();
// Remove nodes that do not have components, these could be versioning artifacts
// or nodes that are missing due to a missing gem
AZStd::vector<AZ::Entity*> nodesToRemove;
for (auto* node : graph->GetGraphData()->m_nodes)
{
if (node->GetComponents().empty())
{
AZ_TracePrintf(ScriptCanvas::k_VersionExplorerWindow.data(), "Removing node due to missing components: %s\nVerify that all gems that this script relies on are enabled\n", node->GetName().c_str());
nodesToRemove.push_back(node);
}
}
for (AZ::Entity* nodeEntity : nodesToRemove)
{
graph->GetGraphData()->m_nodes.erase(nodeEntity);
}
for (const AZ::EntityId& scriptCanvasNodeId : graph->GetNodes())
{
sm->m_assetSanitizationSet.insert(scriptCanvasNodeId);
ScriptCanvas::Node* scriptCanvasNode = graph->FindNode(scriptCanvasNodeId);
sm->m_graphCanvasGraphId = graph->GetGraphCanvasGraphId();
if (scriptCanvasNode)
{
sm->m_allNodes.insert(scriptCanvasNode);
if (scriptCanvasNode->IsDeprecated())
{
sm->m_deprecatedNodes.insert(scriptCanvasNode);
}
if (scriptCanvasNode->IsOutOfDate(graph->GetVersion()))
{
sm->m_outOfDateNodes.insert(scriptCanvasNode);
}
if (scriptCanvasNode->IsSanityCheckRequired())
{
sm->m_sanityCheckRequiredNodes.insert(scriptCanvasNode);
}
}
}
}
IState::ExitStatus CollectData::OnExit()
{
EditorGraphUpgradeMachine* sm = GetStateMachine<EditorGraphUpgradeMachine>();
Log("---------------------------------------------------------------------\n");
Log("Upgrading\n");
Log("%d Nodes\n", sm->m_allNodes.size());
if (sm->m_deprecatedNodes.size() > 0)
{
Log("%d Deprecated\n", sm->m_deprecatedNodes.size());
}
if (sm->m_outOfDateNodes.size() > 0)
{
Log("%d Out of Date\n", sm->m_outOfDateNodes.size());
}
if (sm->m_sanityCheckRequiredNodes.size() > 0)
{
Log("%d Require Additional Checks\n", sm->m_sanityCheckRequiredNodes.size());
}
Log("---------------------------------------------------------------------\n");
return ExitStatus::Default;
}
void PreRequisites::Run()
{
EditorGraphUpgradeMachine* sm = GetStateMachine<EditorGraphUpgradeMachine>();
auto* graph = sm->m_graph;
graph->m_variableDataModel.Activate(sm->m_scriptCanvasId);
graph->ConnectGraphCanvasBuses();
GraphCanvas::SceneRequestBus::Event(sm->m_graphCanvasGraphId, &GraphCanvas::SceneRequests::SignalLoadStart);
auto saveDataIter = graph->m_graphCanvasSaveData.find(graph->GetEntityId());
if (saveDataIter != graph->m_graphCanvasSaveData.end())
{
GraphCanvas::EntitySaveDataRequestBus::Event(sm->m_graphCanvasGraphId, &GraphCanvas::EntitySaveDataRequests::ReadSaveData, (*saveDataIter->second));
}
}
void UpgradeConnections::Run()
{
EditorGraphUpgradeMachine* sm = GetStateMachine<EditorGraphUpgradeMachine>();
auto* graph = sm->m_graph;
AZStd::vector< AZ::EntityId > connectionIds = graph->GetConnections();
for (const AZ::EntityId& connectionId : connectionIds)
{
ScriptCanvas::Endpoint scriptCanvasSourceEndpoint;
ScriptCanvas::Endpoint scriptCanvasTargetEndpoint;
ScriptCanvas::ConnectionRequestBus::EventResult(scriptCanvasSourceEndpoint, connectionId, &ScriptCanvas::ConnectionRequests::GetSourceEndpoint);
ScriptCanvas::ConnectionRequestBus::EventResult(scriptCanvasTargetEndpoint, connectionId, &ScriptCanvas::ConnectionRequests::GetTargetEndpoint);
Log("Upgrade Connection: %s\n", Helpers::ConnectionToText(graph, scriptCanvasSourceEndpoint, scriptCanvasTargetEndpoint).c_str());
AZ::EntityId graphCanvasSourceNode;
auto scriptCanvasIter = sm->m_scriptCanvasToGraphCanvasMapping.find(scriptCanvasSourceEndpoint.GetNodeId());
if (scriptCanvasIter != sm->m_scriptCanvasToGraphCanvasMapping.end())
{
graphCanvasSourceNode = scriptCanvasIter->second;
}
else
{
AZ_Warning(ScriptCanvas::k_VersionExplorerWindow.data(), false, "Could not find ScriptCanvas Node with id %llu", static_cast<AZ::u64>(scriptCanvasSourceEndpoint.GetNodeId()));
}
AZ::EntityId graphCanvasSourceSlotId;
SlotMappingRequestBus::EventResult(graphCanvasSourceSlotId, graphCanvasSourceNode, &SlotMappingRequests::MapToGraphCanvasId, scriptCanvasSourceEndpoint.GetSlotId());
if (!graphCanvasSourceSlotId.IsValid())
{
// For the EBusHandler's I need to remap these to a different visual node.
// Since multiple GraphCanvas nodes depict a single ScriptCanvas EBus node.
if (EBusHandlerNodeDescriptorRequestBus::FindFirstHandler(graphCanvasSourceNode) != nullptr)
{
GraphCanvas::Endpoint graphCanvasEventEndpoint;
EBusHandlerNodeDescriptorRequestBus::EventResult(graphCanvasEventEndpoint, graphCanvasSourceNode, &EBusHandlerNodeDescriptorRequests::MapSlotToGraphCanvasEndpoint, scriptCanvasSourceEndpoint.GetSlotId());
graphCanvasSourceSlotId = graphCanvasEventEndpoint.GetSlotId();
}
if (!graphCanvasSourceSlotId.IsValid())
{
AZ_Warning(ScriptCanvas::k_VersionExplorerWindow.data(), sm->m_deletedNodes.count(scriptCanvasSourceEndpoint.GetNodeId()) > 0, "Could not create connection(%s) for Node(%s).", connectionId.ToString().c_str(), scriptCanvasSourceEndpoint.GetNodeId().ToString().c_str());
graph->DisconnectById(connectionId);
continue;
}
}
GraphCanvas::Endpoint graphCanvasTargetEndpoint;
scriptCanvasIter = sm->m_scriptCanvasToGraphCanvasMapping.find(scriptCanvasTargetEndpoint.GetNodeId());
if (scriptCanvasIter != sm->m_scriptCanvasToGraphCanvasMapping.end())
{
graphCanvasTargetEndpoint.m_nodeId = scriptCanvasIter->second;
}
else
{
AZ_Warning(ScriptCanvas::k_VersionExplorerWindow.data(), false, "Could not find ScriptCanvas Node with id %llu", static_cast<AZ::u64>(scriptCanvasSourceEndpoint.GetNodeId()));
}
SlotMappingRequestBus::EventResult(graphCanvasTargetEndpoint.m_slotId, graphCanvasTargetEndpoint.GetNodeId(), &SlotMappingRequests::MapToGraphCanvasId, scriptCanvasTargetEndpoint.GetSlotId());
if (!graphCanvasTargetEndpoint.IsValid())
{
// For the EBusHandler's I need to remap these to a different visual node.
// Since multiple GraphCanvas nodes depict a single ScriptCanvas EBus node.
if (EBusHandlerNodeDescriptorRequestBus::FindFirstHandler(graphCanvasTargetEndpoint.GetNodeId()) != nullptr)
{
EBusHandlerNodeDescriptorRequestBus::EventResult(graphCanvasTargetEndpoint, graphCanvasTargetEndpoint.GetNodeId(), &EBusHandlerNodeDescriptorRequests::MapSlotToGraphCanvasEndpoint, scriptCanvasTargetEndpoint.GetSlotId());
}
if (!graphCanvasTargetEndpoint.IsValid())
{
AZ_Warning(ScriptCanvas::k_VersionExplorerWindow.data(), sm->m_deletedNodes.count(scriptCanvasTargetEndpoint.GetNodeId()) > 0, "Could not create connection(%s) for Node(%s).", connectionId.ToString().c_str(), scriptCanvasTargetEndpoint.GetNodeId().ToString().c_str());
graph->DisconnectById(connectionId);
continue;
}
}
AZ::EntityId graphCanvasConnectionId;
GraphCanvas::SlotRequestBus::EventResult(graphCanvasConnectionId, graphCanvasSourceSlotId, &GraphCanvas::SlotRequests::DisplayConnectionWithEndpoint, graphCanvasTargetEndpoint);
if (graphCanvasConnectionId.IsValid())
{
AZStd::any* userData = nullptr;
GraphCanvas::ConnectionRequestBus::EventResult(userData, graphCanvasConnectionId, &GraphCanvas::ConnectionRequests::GetUserData);
if (userData)
{
(*userData) = connectionId;
SceneMemberMappingConfigurationRequestBus::Event(graphCanvasConnectionId, &SceneMemberMappingConfigurationRequests::ConfigureMapping, connectionId);
}
}
}
}
void VerifySaveDataVersion::Run()
{
EditorGraphUpgradeMachine* sm = GetStateMachine<EditorGraphUpgradeMachine>();
auto* graph = sm->m_graph;
GraphCanvas::SceneRequestBus::Event(graph->GetGraphCanvasGraphId(), &GraphCanvas::SceneRequests::ProcessEnableDisableQueue);
if (graph->m_graphCanvasSaveVersion != GraphCanvas::EntitySaveDataContainer::CurrentVersion)
{
for (auto saveDataPair : graph->m_graphCanvasSaveData)
{
auto graphCanvasIter = sm->m_scriptCanvasToGraphCanvasMapping.find(saveDataPair.first);
graph->OnSaveDataDirtied(graphCanvasIter->second);
}
graph->m_graphCanvasSaveVersion = GraphCanvas::EntitySaveDataContainer::CurrentVersion;
sm->m_graphNeedsDirtying = true;
}
}
void SanityChecks::Run()
{
EditorGraphUpgradeMachine* sm = GetStateMachine<EditorGraphUpgradeMachine>();
if (!sm->m_sanityCheckRequiredNodes.empty())
{
sm->m_graphNeedsDirtying = true;
}
for (auto node : sm->m_sanityCheckRequiredNodes)
{
if (node)
{
node->SanityCheckDynamicDisplay();
}
}
}
IState::ExitStatus SanityChecks::OnExit()
{
EditorGraphUpgradeMachine* sm = GetStateMachine<EditorGraphUpgradeMachine>();
GraphCanvas::SceneRequestBus::Event(sm->m_graphCanvasGraphId, &GraphCanvas::SceneRequests::SignalLoadEnd);
EditorGraphNotificationBus::Event(sm->m_scriptCanvasId, &EditorGraphNotifications::OnGraphCanvasSceneDisplayed);
return IState::ExitStatus::Default;
}
void UpgradeScriptEvents::Run()
{
EditorGraphUpgradeMachine* sm = GetStateMachine<EditorGraphUpgradeMachine>();
auto* graph = sm->m_graph;
auto currentIter = graph->GetGraphData()->m_scriptEventAssets.begin();
while (currentIter != graph->GetGraphData()->m_scriptEventAssets.end())
{
if (sm->m_assetSanitizationSet.find(currentIter->first) == sm->m_assetSanitizationSet.end())
{
currentIter->second = ScriptEvents::ScriptEventsAssetPtr{};
currentIter = graph->GetGraphData()->m_scriptEventAssets.erase(currentIter);
sm->m_graphNeedsDirtying = true;
}
else
{
++currentIter;
}
}
}
void FixLeakedData::Run()
{
EditorGraphUpgradeMachine* sm = GetStateMachine<EditorGraphUpgradeMachine>();
auto* graph = sm->m_graph;
// Fix up leaked data elements
auto mapIter = graph->m_graphCanvasSaveData.begin();
while (mapIter != graph->m_graphCanvasSaveData.end())
{
// Deleted using the wrong id, which orphaned the SaveData. For now we want to go through and sanitize our save data to avoid keeping around a bunch
// of old save data for no reason.
//
// Need to bypass our internal save data for graph canvas information
if (sm->m_scriptCanvasToGraphCanvasMapping.find(mapIter->first) == sm->m_scriptCanvasToGraphCanvasMapping.end()
&& mapIter->first != graph->GetEntityId())
{
delete mapIter->second;
mapIter = graph->m_graphCanvasSaveData.erase(mapIter);
}
else
{
++mapIter;
}
}
}
void UpdateOutOfDateNodes::Run()
{
EditorGraphUpgradeMachine* sm = GetStateMachine<EditorGraphUpgradeMachine>();
auto* graph = sm->m_graph;
AZStd::unordered_set< AZ::EntityId > graphCanvasNodesToDelete;
for (auto scriptCanvasNode : sm->m_outOfDateNodes)
{
graph->OnVersionConversionBegin((*scriptCanvasNode));
auto graphCanvasNodeId = sm->m_scriptCanvasToGraphCanvasMapping[scriptCanvasNode->GetEntityId()];
ScriptCanvas::UpdateResult updateResult = scriptCanvasNode->UpdateNode();
graph->OnVersionConversionEnd((*scriptCanvasNode));
switch (updateResult)
{
case ScriptCanvas::UpdateResult::DeleteNode:
{
sm->m_graphNeedsDirtying = true;
sm->m_deletedNodes.insert(scriptCanvasNode->GetEntityId());
graphCanvasNodesToDelete.insert(graphCanvasNodeId);
break;
}
default:
{
sm->m_graphNeedsDirtying = true;
break;
}
}
}
if (!graphCanvasNodesToDelete.empty())
{
for (auto& nodeId : sm->m_deletedNodes)
{
ScriptCanvas::Node* node = graph->FindNode(nodeId);
if (node)
{
Log("Deleted: %s\n", node->GetNodeName().c_str());
}
}
GraphCanvas::SceneRequestBus::Event(sm->m_graphCanvasGraphId, &GraphCanvas::SceneRequests::Delete, graphCanvasNodesToDelete);
}
}
void ReplaceDeprecatedConnections::Run()
{
EditorGraphUpgradeMachine* sm = GetStateMachine<EditorGraphUpgradeMachine>();
auto* graph = sm->m_graph;
if (!sm->m_updateReport.IsEmpty())
{
// currently, it is expected that there are no deleted old slots, those need manual correction
AZ_Error(ScriptCanvas::k_VersionExplorerWindow.data(), sm->m_updateReport.m_deletedOldSlots.empty(), "Graph upgrade path: If old slots are deleted, manual upgrading is required");
UpdateConnectionStatus(*graph, sm->m_updateReport);
}
}
void ReplaceDeprecatedNodes::Run()
{
EditorGraphUpgradeMachine* sm = GetStateMachine<EditorGraphUpgradeMachine>();
auto* graph = sm->m_graph;
for (auto& node : sm->m_deprecatedNodes)
{
ScriptCanvas::NodeConfiguration nodeConfig = node->GetReplacementNodeConfiguration();
if (nodeConfig.IsValid())
{
ScriptCanvas::NodeUpdateSlotReport nodeUpdateSlotReport;
auto nodeEntity = node->GetEntityId();
auto nodeOutcome = graph->ReplaceNodeByConfig(node, nodeConfig, nodeUpdateSlotReport);
if (nodeOutcome.IsSuccess())
{
ScriptCanvas::MergeUpdateSlotReport(nodeEntity, 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();
sm->m_allNodes.insert(replacedNode);
if (replacedNode->IsOutOfDate(graph->GetVersion()))
{
sm->m_outOfDateNodes.insert(replacedNode);
}
if (replacedNode->IsSanityCheckRequired())
{
sm->m_sanityCheckRequiredNodes.insert(replacedNode);
}
Log("Replaced node (%s)\n", replacedNode->GetNodeName().c_str());
}
}
}
}
void BuildGraphCanvasMapping::Run()
{
EditorGraphUpgradeMachine* sm = GetStateMachine<EditorGraphUpgradeMachine>();
auto* graph = sm->m_graph;
for (auto& scriptCanvasNode : sm->m_allNodes)
{
auto scriptCanvasNodeId = scriptCanvasNode->GetEntityId();
AZ::EntityId graphCanvasNodeId = Nodes::DisplayScriptCanvasNode(sm->m_graphCanvasGraphId, scriptCanvasNode);
sm->m_scriptCanvasToGraphCanvasMapping[scriptCanvasNodeId] = graphCanvasNodeId;
auto saveDataIter = graph->m_graphCanvasSaveData.find(scriptCanvasNodeId);
if (saveDataIter != graph->m_graphCanvasSaveData.end())
{
GraphCanvas::EntitySaveDataRequestBus::Event(graphCanvasNodeId, &GraphCanvas::EntitySaveDataRequests::ReadSaveData, (*saveDataIter->second));
}
AZ::Vector2 position;
GraphCanvas::GeometryRequestBus::EventResult(position, graphCanvasNodeId, &GraphCanvas::GeometryRequests::GetPosition);
GraphCanvas::SceneRequestBus::Event(sm->m_graphCanvasGraphId, &GraphCanvas::SceneRequests::AddNode, graphCanvasNodeId, position, false);
// If the node is deprecated, we want to stomp whatever style it had saved and apply the deprecated style
if (scriptCanvasNode->IsDeprecated())
{
Log("Marking node deprecated: %s\n", scriptCanvasNode->GetNodeName().c_str());
GraphCanvas::NodeTitleRequestBus::Event(graphCanvasNodeId, &GraphCanvas::NodeTitleRequests::SetPaletteOverride, "DeprecatedNodeTitlePalette");
}
}
}
void ParseGraph::Run()
{
EditorGraphUpgradeMachine* sm = GetStateMachine<EditorGraphUpgradeMachine>();
auto* graph = sm->m_graph;
ScriptCanvas::ValidationResults validationResults;
// Disable the g_saveRawTranslationOuputToFile CVar during Parse (not needed for upgrade) and restore to its set value after
bool saveRawTranslationOuputToFile = ScriptCanvas::Grammar::g_saveRawTranslationOuputToFile;
ScriptCanvas::Grammar::g_saveRawTranslationOuputToFile = false;
// save parsing status before after, just because it didn't parse after doesn't mean it didn't before
graph->Parse(validationResults);
ScriptCanvas::Grammar::g_saveRawTranslationOuputToFile = saveRawTranslationOuputToFile;
if (validationResults.HasErrors())
{
sm->MarkError("Failed to Parse");
for (auto& err : validationResults.GetEvents())
{
// Register this graph as needing manual updates
Log("%s: %s\n", err->GetIdentifier().c_str(), err->GetDescription().data());
}
}
}
void RestoreUndo::Run()
{
GeneralRequestBus::Broadcast(&GeneralRequests::PopPreventUndoStateUpdate);
}
void Finalize::Run()
{
EditorGraphUpgradeMachine* sm = GetStateMachine<EditorGraphUpgradeMachine>();
auto* graph = sm->m_graph;
if (sm->m_graphNeedsDirtying)
{
graph->SignalDirty();
}
graph->MarkVersion();
}
void DisplayReport::Run()
{
Log("Upgrade Complete\n\n\n");
}
void Skip::Run()
{
Log("Up to date (skipped)\n");
}
// Transitions
int Start::EvaluateTransition()
{
return PreventUndo::StateID();
}
int PreventUndo::EvaluateTransition()
{
return PreRequisites::StateID();
}
int PreRequisites::EvaluateTransition()
{
return CollectData::StateID();
}
int CollectData::EvaluateTransition()
{
return ReplaceDeprecatedNodes::StateID();
}
int ReplaceDeprecatedNodes::EvaluateTransition()
{
return BuildGraphCanvasMapping::StateID();
}
int BuildGraphCanvasMapping::EvaluateTransition()
{
return ReplaceDeprecatedConnections::StateID();
}
int ReplaceDeprecatedConnections::EvaluateTransition()
{
return UpdateOutOfDateNodes::StateID();
}
int UpdateOutOfDateNodes::EvaluateTransition()
{
return UpgradeConnections::StateID();
}
int UpgradeConnections::EvaluateTransition()
{
return FixLeakedData::StateID();
}
int FixLeakedData::EvaluateTransition()
{
return UpgradeScriptEvents::StateID();
}
int UpgradeScriptEvents::EvaluateTransition()
{
return SanityChecks::StateID();
}
int SanityChecks::EvaluateTransition()
{
return VerifySaveDataVersion::StateID();
}
int VerifySaveDataVersion::EvaluateTransition()
{
return RestoreUndo::StateID();
}
int RestoreUndo::EvaluateTransition()
{
return Finalize::StateID();
}
int Finalize::EvaluateTransition()
{
return ParseGraph::StateID();
}
int ParseGraph::EvaluateTransition()
{
return DisplayReport::StateID();
}
int DisplayReport::EvaluateTransition()
{
return EXIT_STATE_ID;
}
int Skip::EvaluateTransition()
{
return EXIT_STATE_ID;
}
//
#define RegisterState(stateName) m_states.emplace_back(new stateName(this));
EditorGraphUpgradeMachine::EditorGraphUpgradeMachine(EditorGraph* graph)
: m_graph(graph)
{
RegisterState(Start);
RegisterState(PreRequisites);
RegisterState(PreventUndo);
RegisterState(CollectData);
RegisterState(ReplaceDeprecatedNodes);
RegisterState(ReplaceDeprecatedConnections);
RegisterState(VerifySaveDataVersion);
RegisterState(SanityChecks);
RegisterState(UpgradeScriptEvents);
RegisterState(UpdateOutOfDateNodes);
RegisterState(UpgradeConnections);
RegisterState(BuildGraphCanvasMapping);
RegisterState(FixLeakedData);
RegisterState(RestoreUndo);
RegisterState(Finalize);
RegisterState(DisplayReport);
RegisterState(Skip);
RegisterState(ParseGraph);
}
void EditorGraphUpgradeMachine::SetAsset(SourceHandle& asset)
{
if (m_asset != asset)
{
m_asset = asset;
SetDebugPrefix(asset.Path().c_str());
}
}
void EditorGraphUpgradeMachine::OnComplete(IState::ExitStatus exitStatus)
{
UpgradeNotificationsBus::Broadcast(&UpgradeNotifications::OnGraphUpgradeComplete, m_asset, exitStatus == IState::ExitStatus::Skipped);
// releasing the asset at this stage of the system tick causes a memory crash
// m_asset = {};
}
//////////////////////////////////////////////////////////////////////
// State Machine Internals
bool StateMachine::GetVerbose() const
{
return m_isVerbose;
}
void StateMachine::OnSystemTick()
{
IState::ExitStatus exitStatus = IState::ExitStatus::Default;
if (m_currentState)
{
m_currentState->Run();
int targetState = m_currentState->EvaluateTransition();
if (targetState != IState::EXIT_STATE_ID)
{
exitStatus = m_currentState->Exit();
auto state = AZStd::find_if(m_states.begin(), m_states.end(), [targetState](AZStd::shared_ptr<IState>& state) { if (state->GetStateId() == targetState) { return true; } return false; });
if (state != m_states.end())
{
m_currentState = *state;
m_currentState->Enter();
}
else
{
AZ_Assert(false, "Target State ID: %d Not Registered (From: %s)", targetState, m_currentState->GetName());
m_currentState = nullptr;
}
}
else
{
exitStatus = m_currentState->Exit();
m_currentState = nullptr;
}
}
if (!m_currentState)
{
AZ::SystemTickBus::Handler::BusDisconnect();
OnComplete(m_error.empty() ? exitStatus : IState::ExitStatus::Skipped);
}
}
void StateMachine::Run(int startStateID)
{
auto startState = AZStd::find_if(m_states.begin(), m_states.end(), [startStateID](AZStd::shared_ptr<IState>& state) { if (state->GetStateId() == startStateID) { return true; } return false; });
if (startState != m_states.end())
{
m_currentState = *startState;
m_currentState->Enter();
AZ::SystemTickBus::Handler::BusConnect();
}
}
void StateMachine::SetVerbose(bool isVerbose)
{
m_isVerbose = isVerbose;
}
const AZStd::string& StateMachine::GetDebugPrefix() const
{
return m_debugPrefix;
}
void StateMachine::SetDebugPrefix(AZStd::string_view prefix)
{
m_debugPrefix = prefix;
}
}