/* * 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 #include #include #include #include #include #include #include #include #include #include #include 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(); 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(); 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 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(); 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(); 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(); 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(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(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(); 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(); 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(); 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(); 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(); 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(); 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(); 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(); auto* graph = sm->m_graph; for (auto& node : sm->m_deprecatedNodes) { ScriptCanvas::NodeReplacementConfiguration nodeConfig; auto replacementId = ScriptCanvasEditor::NodeReplacementSystem::GenerateReplacementId(node); ScriptCanvasEditor::NodeReplacementRequestBus::BroadcastResult( nodeConfig, &ScriptCanvasEditor::NodeReplacementRequestBus::Events::GetNodeReplacementConfiguration, replacementId); if (!nodeConfig.IsValid()) { nodeConfig = node->GetReplacementNodeConfiguration(); } if (nodeConfig.IsValid()) { auto nodeEntity = node->GetEntityId(); ScriptCanvas::NodeUpdateReport nodeUpdateReport; ScriptCanvasEditor::NodeReplacementRequestBus::BroadcastResult(nodeUpdateReport, &ScriptCanvasEditor::NodeReplacementRequestBus::Events::ReplaceNodeByReplacementConfiguration, graph->GetScriptCanvasId(), node, nodeConfig); if (!nodeUpdateReport.IsEmpty()) { ScriptCanvas::MergeUpdateSlotReport(nodeEntity, sm->m_updateReport, nodeUpdateReport); sm->m_allNodes.erase(node); sm->m_outOfDateNodes.erase(node); sm->m_sanityCheckRequiredNodes.erase(node); sm->m_graphNeedsDirtying = true; auto replacedNode = nodeUpdateReport.m_newNode; 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(); 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(); 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()) { if (!sm->GetConfig().saveParseErrors) { 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(); 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.RelativePath().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 const UpgradeGraphConfig& StateMachine::GetConfig() const { return m_config; } 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& 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& 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::SetConfig(const UpgradeGraphConfig& config) { m_config = config; } const AZStd::string& StateMachine::GetDebugPrefix() const { return m_debugPrefix; } void StateMachine::SetDebugPrefix(AZStd::string_view prefix) { m_debugPrefix = prefix; } }