diff --git a/Gems/SimulationInterfaces/Code/Include/SimulationInterfaces/LevelManagerRequestBus.h b/Gems/SimulationInterfaces/Code/Include/SimulationInterfaces/LevelManagerRequestBus.h new file mode 100644 index 0000000000..951fca8ee4 --- /dev/null +++ b/Gems/SimulationInterfaces/Code/Include/SimulationInterfaces/LevelManagerRequestBus.h @@ -0,0 +1,79 @@ +/* + * 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 + * + */ + +#pragma once + +#include "SimulationInterfacesTypeIds.h" +#include +#include +#include +#include +#include +#include + +#include "WorldResource.h" +#include +#include +#include +#include + +namespace SimulationInterfaces +{ + //! @see GetAvailableWorlds.srv + struct GetWorldsRequest + { + // Optional sources (local or remote) where simulator should look for worlds + AZStd::vector additionalSources; + TagFilter filter; // A filter to match against world tags + bool offlineOnly = false; // Whether to limit search to only offline resources + bool continueOnError = false; // By default fail in case of missing asset + }; + + //! @see LoadWorld.srv + struct LoadWorldRequest + { + Resource levelResource; // which level needs to be loaded + bool failOnUnsupportedElement = false; // By default just skip failed elements + bool ignoreMissingOrUnsupportedAssets = false; // Whether search should be continue on some noncritical fails + }; + + using WorldResourcesList = AZStd::vector; + + class LevelManagerRequests + { + public: + AZ_RTTI(LevelManagerRequests, LevelManagerRequestsTypeId); + virtual ~LevelManagerRequests() = default; + + //! Returns vector with all worlds available in the simulator + virtual AZ::Outcome GetAvailableWorlds(const GetWorldsRequest& request) = 0; + //! Returns currently loaded world. If no world is loaded, error is returned + virtual AZ::Outcome GetCurrentWorld() = 0; + //! Loads world based on provided resources + virtual AZ::Outcome LoadWorld(const LoadWorldRequest& request) = 0; + //! Unloads currently loaded level + virtual AZ::Outcome UnloadWorld() = 0; + //! Reloads current level + virtual void ReloadLevel() = 0; + }; + + class LevelManagerRequestBusTraits : public AZ::EBusTraits + { + public: + ////////////////////////////////////////////////////////////////////////// + // EBusTraits overrides + static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + ////////////////////////////////////////////////////////////////////////// + }; + + using LevelManagerRequestBus = AZ::EBus; + +} // namespace SimulationInterfaces diff --git a/Gems/SimulationInterfaces/Code/Include/SimulationInterfaces/Resource.h b/Gems/SimulationInterfaces/Code/Include/SimulationInterfaces/Resource.h new file mode 100644 index 0000000000..619b20d9d4 --- /dev/null +++ b/Gems/SimulationInterfaces/Code/Include/SimulationInterfaces/Resource.h @@ -0,0 +1,27 @@ +/* + * 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 + * + */ + +#pragma once +#include + +namespace SimulationInterfaces +{ + //! A message type to represent simulation Resource + //! @see Resource.msg + struct Resource + { + Resource() = default; + Resource(AZStd::string uri, AZStd::string resourceString) + : m_uri(uri) + , m_resourceString(resourceString) + { + } + AZStd::string m_uri; + AZStd::string m_resourceString; + }; +} // namespace SimulationInterfaces diff --git a/Gems/SimulationInterfaces/Code/Include/SimulationInterfaces/SimulationEntityManagerRequestBus.h b/Gems/SimulationInterfaces/Code/Include/SimulationInterfaces/SimulationEntityManagerRequestBus.h index 08514e8ba3..30e8516048 100644 --- a/Gems/SimulationInterfaces/Code/Include/SimulationInterfaces/SimulationEntityManagerRequestBus.h +++ b/Gems/SimulationInterfaces/Code/Include/SimulationInterfaces/SimulationEntityManagerRequestBus.h @@ -105,7 +105,7 @@ namespace SimulationInterfaces //! Reset the simulation to begin. //! This will revert the entire simulation to the initial state. - virtual void ResetAllEntitiesToInitialState() = 0; + virtual AZ::Outcome ResetAllEntitiesToInitialState() = 0; }; class SimulationInterfacesBusTraits : public AZ::EBusTraits diff --git a/Gems/SimulationInterfaces/Code/Include/SimulationInterfaces/SimulationInterfacesTypeIds.h b/Gems/SimulationInterfaces/Code/Include/SimulationInterfaces/SimulationInterfacesTypeIds.h index 66cd251dcc..1f6fde8774 100644 --- a/Gems/SimulationInterfaces/Code/Include/SimulationInterfaces/SimulationInterfacesTypeIds.h +++ b/Gems/SimulationInterfaces/Code/Include/SimulationInterfaces/SimulationInterfacesTypeIds.h @@ -23,6 +23,8 @@ namespace SimulationInterfaces inline constexpr const char* NamedPoseManagerTypeId = "{0F100078-C297-482C-BA56-644AB09F053F}"; inline constexpr const char* NamedPoseManagerEditorTypeId = "{9E91A513-E1A5-43F0-B7B1-A759EEC23174}"; + inline constexpr const char* LevelManagerTypeId = "{A9D5953D-F266-46D4-AD08-82CF10F426F7}"; + inline constexpr const char* LevelManagerEditorTypeId = "{16C16216-279D-4215-9DF8-8AB4F15AC4C8}"; // Module derived classes TypeIds inline constexpr const char* SimulationInterfacesModuleInterfaceTypeId = "{675797BF-E5D5-438A-BF86-4B4554F09CEF}"; inline constexpr const char* SimulationInterfacesModuleTypeId = "{8D6741FD-3105-4CB0-9700-152123B6D135}"; @@ -41,5 +43,6 @@ namespace SimulationInterfaces inline constexpr const char* NamedPoseTypeId = "{C6B4A8BE-F39A-46ED-82E8-05BC260A1F31}"; inline constexpr const char* NamedPoseComponentTypeId = "{A294B840-79CA-402D-A250-B5C3D958B518}"; inline constexpr const char* NamedPoseEditorComponentTypeId = "{867002FC-ECA9-4CAE-A7B4-D2CA6FA14EF6}"; + inline constexpr const char* LevelManagerRequestsTypeId = "{88519292-D032-4A2F-B323-FEDFE2E277EA}"; } // namespace SimulationInterfaces diff --git a/Gems/SimulationInterfaces/Code/Include/SimulationInterfaces/SimulationMangerRequestBus.h b/Gems/SimulationInterfaces/Code/Include/SimulationInterfaces/SimulationMangerRequestBus.h index deb5c7629c..cc2076d79c 100644 --- a/Gems/SimulationInterfaces/Code/Include/SimulationInterfaces/SimulationMangerRequestBus.h +++ b/Gems/SimulationInterfaces/Code/Include/SimulationInterfaces/SimulationMangerRequestBus.h @@ -9,6 +9,7 @@ #pragma once #include "SimulationInterfacesTypeIds.h" +#include #include "Result.h" #include @@ -30,7 +31,7 @@ namespace SimulationInterfaces using ReloadLevelCallback = AZStd::function; //! Reload level and remove all spawned simulation entities. - virtual void ReloadLevel(ReloadLevelCallback completionCallback) = 0; + virtual AZ::Outcome ResetSimulation(ReloadLevelCallback completionCallback) = 0; //! Set the simulation to paused or unpaused, //! expect always to succeed @@ -61,6 +62,9 @@ namespace SimulationInterfaces //! https://github.com/ros-simulation/simulation_interfaces/blob/main/msg/SimulationState.msg //! @return outcome indicating if setting state succeed. In case of failure error message with error code is returned virtual AZ::Outcome SetSimulationState(SimulationState stateToSet) = 0; + //! Check if it possible to work with entities. These operations should be possible only if world is loaded + //! @return bool indicating if simulator is ready for taking requests related to spawning/despawning setting entity state etc + virtual bool EntitiesOperationsPossible() = 0; }; class SimulationMangerRequestBusTraits : public AZ::EBusTraits diff --git a/Gems/SimulationInterfaces/Code/Include/SimulationInterfaces/WorldResource.h b/Gems/SimulationInterfaces/Code/Include/SimulationInterfaces/WorldResource.h new file mode 100644 index 0000000000..fc57cd812f --- /dev/null +++ b/Gems/SimulationInterfaces/Code/Include/SimulationInterfaces/WorldResource.h @@ -0,0 +1,33 @@ +/* + * 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 + * + */ + +#pragma once +#include "Resource.h" +#include +#include + +namespace SimulationInterfaces +{ + //! A message type to represent simulation Resource + //! @see Resource.msg + struct WorldResource + { + WorldResource() = default; + WorldResource(AZStd::string name, Resource resource, AZStd::string description, AZStd::vector tags) + : m_name(name) + , m_worldResource(resource) + , m_description(description) + , m_tags(tags) + { + } + AZStd::string m_name; + Resource m_worldResource; + AZStd::string m_description; + AZStd::vector m_tags; + }; +} // namespace SimulationInterfaces diff --git a/Gems/SimulationInterfaces/Code/Source/Clients/LevelManager.cpp b/Gems/SimulationInterfaces/Code/Source/Clients/LevelManager.cpp new file mode 100644 index 0000000000..c5c38fb918 --- /dev/null +++ b/Gems/SimulationInterfaces/Code/Source/Clients/LevelManager.cpp @@ -0,0 +1,355 @@ +/* + * 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 "LevelManager.h" +#include "SimulationInterfaces/SimulationMangerRequestBus.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// ordering important +// clang-format off +#include +#include +// clang-format on + +namespace SimulationInterfaces +{ + namespace + { + ILevelSystem* GetLevelSystem() + { + ISystem* iSystem = GetISystem(); + return (iSystem != nullptr) ? iSystem->GetILevelSystem() : nullptr; + } + + AZStd::string GetLevelNameFromAssetPath(const AZStd::string& assetPath) + { + AZ::IO::PathView levelPath{ assetPath }; + return levelPath.Stem().Native(); + } + } // namespace + + AZ_COMPONENT_IMPL(LevelManager, "LevelManager", LevelManagerTypeId); + + void LevelManager::Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class()->Version(0); + } + } + + void LevelManager::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC_CE("LevelManagerService")); + } + + void LevelManager::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC_CE("LevelManagerService")); + } + + void LevelManager::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required) + { + required.push_back(AZ_CRC_CE("AssetCatalogService")); + required.push_back(AZ_CRC_CE("SimulationFeaturesAggregator")); + } + + void LevelManager::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + dependent.push_back(AZ_CRC_CE("SimulationFeaturesAggregator")); + } + + void LevelManager::Activate() + { + LevelManagerRequestBus::Handler::BusConnect(); + AZ::ApplicationTypeQuery appType; + AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationBus::Events::QueryApplicationType, appType); + m_isAppEditor = (appType.IsValid() && appType.IsEditor()); + + SimulationFeaturesAggregatorRequestBus::Broadcast( + &SimulationFeaturesAggregatorRequests::AddSimulationFeatures, + AZStd::unordered_set{ simulation_interfaces::msg::SimulatorFeatures::AVAILABLE_WORLDS }); + + // level loading and unloading is supported only in GameLauncher, remove features from Editor application + if (!m_isAppEditor) + { + SimulationFeaturesAggregatorRequestBus::Broadcast( + &SimulationFeaturesAggregatorRequests::AddSimulationFeatures, + AZStd::unordered_set{ simulation_interfaces::msg::SimulatorFeatures::WORLD_INFO_GETTING, + simulation_interfaces::msg::SimulatorFeatures::WORLD_LOADING, + simulation_interfaces::msg::SimulatorFeatures::WORLD_UNLOADING }); + } + } + void LevelManager::Deactivate() + { + LevelManagerRequestBus::Handler::BusDisconnect(); + } + + AZ::Outcome LevelManager::GetAvailableWorlds(const GetWorldsRequest& request) + { + WorldResourcesList availableWorlds; + // request validation + if (!request.additionalSources.empty()) + { + constexpr const char* errorMsg = "Additional Sources are not implemented yet"; + AZ_Warning("SimulationInterfaces", false, errorMsg); + return AZ::Failure(FailedResult(simulation_interfaces::msg::Result::RESULT_FEATURE_UNSUPPORTED, errorMsg)); + } + + if (!request.filter.m_tags.empty()) + { + constexpr const char* errorMsg = "Tags filter is not implemented yet"; + AZ_Warning("SimulationInterfaces", false, errorMsg); + return AZ::Failure(FailedResult(simulation_interfaces::msg::Result::RESULT_FEATURE_UNSUPPORTED, errorMsg)); + } + + if (!request.offlineOnly) + { + constexpr const char* errorMsg = "Online search is not implemented yet, only pure offline search is supported"; + AZ_Warning("SimulationInterfaces", false, errorMsg); + return AZ::Failure(FailedResult(simulation_interfaces::msg::Result::RESULT_FEATURE_UNSUPPORTED, errorMsg)); + } + + const auto allLevels = GetAllAvailableLevels(); + if (!allLevels.IsSuccess()) + { + return AZ::Failure(allLevels.GetError()); + } + for (const auto& levelPath : allLevels.GetValue()) + { + availableWorlds.emplace_back( + GetLevelNameFromAssetPath(levelPath), Resource{ levelPath, "" }, "", AZStd::vector{}); + } + return AZ::Success(availableWorlds); + } + + AZ::Outcome LevelManager::GetCurrentWorld() + { + if (m_isAppEditor) + { + constexpr const char* errorMsg = "GetCurrentWorld is not supported in Editor"; + AZ_Warning("SimulationInterfaces", false, errorMsg); + return AZ::Failure(FailedResult(simulation_interfaces::msg::Result::RESULT_FEATURE_UNSUPPORTED, errorMsg)); + } + + WorldResource currentWorld; + auto* levelInterface = AzFramework::LevelSystemLifecycleInterface::Get(); + if (levelInterface == nullptr) + { + constexpr const char* errorMsg = "Failed to get level interface"; + AZ_Warning("SimulationInterfaces", false, errorMsg); + return AZ::Failure(FailedResult(simulation_interfaces::srv::GetAvailableWorlds::Response::DEFAULT_SOURCES_FAILED, errorMsg)); + } + + if (!levelInterface->IsLevelLoaded()) + { + constexpr const char* errorMsg = "No level loaded"; + AZ_Warning("SimulationInterfaces", false, errorMsg); + return AZ::Failure(FailedResult(simulation_interfaces::srv::GetCurrentWorld::Response::NO_WORLD_LOADED, errorMsg)); + } + + const auto* levelPath = levelInterface->GetCurrentLevelName(); + if (levelPath == nullptr) + { + constexpr const char* errorMsg = "Failed to get current level path"; + AZ_Warning("SimulationInterfaces", false, errorMsg); + return AZ::Failure(FailedResult(simulation_interfaces::msg::Result::RESULT_OPERATION_FAILED, errorMsg)); + } + AZStd::string levelPathStr{ levelPath }; + AZStd::to_lower(levelPathStr.begin(), levelPathStr.end()); + auto levelName = GetLevelNameFromAssetPath(levelPathStr); + currentWorld.m_worldResource.m_uri = levelPathStr; + currentWorld.m_name = levelName; + return AZ::Success(currentWorld); + } + + AZ::Outcome LevelManager::LoadWorld(const LoadWorldRequest& request) + { + if (m_isAppEditor) + { + constexpr const char* errorMsg = "LoadWorld is not supported in Editor"; + AZ_Warning("SimulationInterfaces", false, errorMsg); + return AZ::Failure(FailedResult(simulation_interfaces::msg::Result::RESULT_FEATURE_UNSUPPORTED, errorMsg)); + } + + WorldResource loadedWorld; + + if (request.levelResource.m_resourceString.empty() && request.levelResource.m_uri.empty()) + { + constexpr const char* errorMsg = "uri and resource string in levelResource cannot be both empty"; + AZ_Warning("SimulationInterfaces", false, errorMsg); + return AZ::Failure(FailedResult(simulation_interfaces::srv::LoadWorld::Response::NO_RESOURCE, errorMsg)); + } + if (!request.levelResource.m_resourceString.empty() && !request.levelResource.m_uri.empty()) + { + constexpr const char* errorMsg = "uri and resource string in levelResource cannot be both non-empty"; + AZ_Warning("SimulationInterfaces", false, errorMsg); + return AZ::Failure(FailedResult(simulation_interfaces::msg::Result::RESULT_OPERATION_FAILED, errorMsg)); + } + if (!request.levelResource.m_resourceString.empty()) + { + constexpr const char* errorMsg = "Loading world from resource string is not implemented yet"; + AZ_Warning("SimulationInterfaces", false, errorMsg); + return AZ::Failure(FailedResult(simulation_interfaces::msg::Result::RESULT_FEATURE_UNSUPPORTED, errorMsg)); + } + + const auto levelNamesResult = GetAllAvailableLevels(); + if (!levelNamesResult.IsSuccess()) + { + return AZ::Failure(levelNamesResult.GetError()); + } + const auto& levelsPaths = levelNamesResult.GetValue(); + + if (AZStd::find(levelsPaths.begin(), levelsPaths.end(), request.levelResource.m_uri) == levelsPaths.end()) + { + const AZStd::string errorMsg = AZStd::string::format("Requested world/level %s not found", request.levelResource.m_uri.c_str()); + AZ_Warning("SimulationInterfaces", false, errorMsg.c_str()); + return AZ::Failure(FailedResult(simulation_interfaces::srv::LoadWorld::Response::MISSING_ASSETS, errorMsg)); + } + + ILevelSystem* levelSystem = GetLevelSystem(); + if (levelSystem == nullptr) + { + constexpr const char* errorMsg = "Failed to start, level System not available"; + AZ_Warning("SimulationInterfaces", false, errorMsg); + return AZ::Failure(FailedResult(simulation_interfaces::msg::Result::RESULT_OPERATION_FAILED, errorMsg)); + } + + // unload level if needed + if (GetCurrentWorld().IsSuccess()) + { + levelSystem->UnloadLevel(); + } + // notify state machine + SimulationManagerRequestBus::Broadcast( + &SimulationManagerRequests::SetSimulationState, simulation_interfaces::msg::SimulationState::STATE_LOADING_WORLD); + + if (!levelSystem->LoadLevel(request.levelResource.m_uri.c_str())) + { + constexpr const char* errorMsg = "Failed to load world"; + AZ_Warning("SimulationInterfaces", false, errorMsg); + SimulationManagerRequestBus::Broadcast( + &SimulationManagerRequests::SetSimulationState, simulation_interfaces::msg::SimulationState::STATE_NO_WORLD); + return AZ::Failure(FailedResult(simulation_interfaces::srv::LoadWorld::Response::RESOURCE_PARSE_ERROR, errorMsg)); + } + // notify state machine + SimulationManagerRequestBus::Broadcast( + &SimulationManagerRequests::SetSimulationState, simulation_interfaces::msg::SimulationState::STATE_STOPPED); + // fill up response + loadedWorld.m_worldResource = request.levelResource; + return AZ::Success(loadedWorld); + } + + AZ::Outcome LevelManager::UnloadWorld() + { + if (m_isAppEditor) + { + constexpr const char* errorMsg = "UnloadWorld is not supported in Editor"; + AZ_Warning("SimulationInterfaces", false, errorMsg); + return AZ::Failure(FailedResult(simulation_interfaces::msg::Result::RESULT_FEATURE_UNSUPPORTED, errorMsg)); + } + const auto currentLevel = GetCurrentWorld(); + if (!currentLevel.IsSuccess()) + { + if (currentLevel.GetError().m_errorCode == simulation_interfaces::srv::GetCurrentWorld::Response::NO_WORLD_LOADED) + { + constexpr const char* errorMsg = "No level loaded"; + AZ_Warning("SimulationInterfaces", false, errorMsg); + return AZ::Failure(FailedResult(simulation_interfaces::srv::UnloadWorld::Response::NO_WORLD_LOADED, errorMsg)); + } + } + ILevelSystem* levelSystem = GetLevelSystem(); + if (!levelSystem) + { + constexpr const char* errorMsg = "Level system is not available"; + AZ_Warning("SimulationInterfaces", false, errorMsg); + return AZ::Failure(FailedResult(simulation_interfaces::msg::Result::RESULT_OPERATION_FAILED, errorMsg)); + } + + levelSystem->UnloadLevel(); + SimulationManagerRequestBus::Broadcast( + &SimulationManagerRequests::SetSimulationState, simulation_interfaces::msg::SimulationState::STATE_NO_WORLD); + return AZ::Success(); + } + + void LevelManager::ReloadLevel() + { + if (m_isAppEditor) + { + constexpr const char* errorMsg = "ReloadWorld is not supported in Editor"; + AZ_Warning("SimulationInterfaces", false, errorMsg); + return; + } + auto levelGathering = GetCurrentWorld(); + if (!levelGathering.IsSuccess()) + { + AZ_Error("LevelManager", false, "Error occurred during level gathering: %s", levelGathering.GetError().m_errorString.c_str()); + return; + } + UnloadWorld(); + LoadWorldRequest request; + request.levelResource = levelGathering.GetValue().m_worldResource; + LoadWorld(request); + } + + AZ::Outcome, FailedResult> LevelManager::GetAllAvailableLevels() + { + ILevelSystem* levelSystem = GetLevelSystem(); + if (!levelSystem) + { + constexpr const char* errorMsg = "Level system is not available"; + AZ_Warning("SimulationInterfaces", false, errorMsg); + return AZ::Failure(FailedResult(simulation_interfaces::srv::GetAvailableWorlds::Response::DEFAULT_SOURCES_FAILED, errorMsg)); + } + // Run through all the assets in the asset catalog and gather up the list of level assets + + AZ::Data::AssetType levelAssetType = levelSystem->GetLevelAssetType(); + AZStd::vector levelNames; + auto enumerateCB = [levelAssetType, &levelNames](const AZ::Data::AssetId id, const AZ::Data::AssetInfo& assetInfo) + { + if (assetInfo.m_assetType != levelAssetType) + { + return; + } + AZStd::string assetPath; + AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetPath, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetPathById, id); + AZStd::string lowerCaseLevelPath = assetPath; + AZStd::to_lower(lowerCaseLevelPath.begin(), lowerCaseLevelPath.end()); + if (!lowerCaseLevelPath.starts_with("levels")) + { + return; + } + levelNames.push_back(assetPath); + }; + + AZ::Data::AssetCatalogRequestBus::Broadcast( + &AZ::Data::AssetCatalogRequestBus::Events::EnumerateAssets, nullptr, enumerateCB, nullptr); + + return AZ::Success(levelNames); + } +} // namespace SimulationInterfaces diff --git a/Gems/SimulationInterfaces/Code/Source/Clients/LevelManager.h b/Gems/SimulationInterfaces/Code/Source/Clients/LevelManager.h new file mode 100644 index 0000000000..c40e3d6752 --- /dev/null +++ b/Gems/SimulationInterfaces/Code/Source/Clients/LevelManager.h @@ -0,0 +1,48 @@ +/* + * 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 + * + */ + +#pragma once +#include +#include + +namespace SimulationInterfaces +{ + class LevelManager + : public AZ::Component + , protected LevelManagerRequestBus::Handler + { + public: + AZ_COMPONENT_DECL(LevelManager); + + static void Reflect(AZ::ReflectContext* context); + + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + + LevelManager() = default; + ~LevelManager() = default; + + protected: + // AZ::Component interface implementation + void Activate() override; + void Deactivate() override; + + private: + // LevelManagerRequestBus interface implementation + AZ::Outcome GetAvailableWorlds(const GetWorldsRequest& request) override; + AZ::Outcome GetCurrentWorld() override; + AZ::Outcome LoadWorld(const LoadWorldRequest& request) override; + AZ::Outcome UnloadWorld() override; + void ReloadLevel() override; + + AZ::Outcome, FailedResult> GetAllAvailableLevels(); + bool m_isAppEditor = false; + }; +} // namespace SimulationInterfaces diff --git a/Gems/SimulationInterfaces/Code/Source/Clients/ROS2SimulationInterfacesSystemComponent.cpp b/Gems/SimulationInterfaces/Code/Source/Clients/ROS2SimulationInterfacesSystemComponent.cpp index 71df594ba8..7a95407ada 100644 --- a/Gems/SimulationInterfaces/Code/Source/Clients/ROS2SimulationInterfacesSystemComponent.cpp +++ b/Gems/SimulationInterfaces/Code/Source/Clients/ROS2SimulationInterfacesSystemComponent.cpp @@ -7,6 +7,7 @@ */ #include "ROS2SimulationInterfacesSystemComponent.h" +#include #include #include @@ -19,6 +20,8 @@ #include #include +#include +#include #include #include #include @@ -27,12 +30,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include namespace ROS2SimulationInterfaces { @@ -71,6 +76,10 @@ namespace ROS2SimulationInterfaces void ROS2SimulationInterfacesSystemComponent::Activate() { ROS2SimulationInterfacesRequestBus::Handler::BusConnect(); + AZ::ApplicationTypeQuery appType; + bool isAppEditor; + AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationBus::Events::QueryApplicationType, appType); + isAppEditor = (appType.IsValid() && appType.IsEditor()); rclcpp::Node::SharedPtr ros2Node = rclcpp::Node::SharedPtr(ROS2::ROS2Interface::Get()->GetNode()); AZ_Assert(ros2Node, "ROS2 node is not available."); @@ -90,6 +99,15 @@ namespace ROS2SimulationInterfaces RegisterInterface(ros2Node); RegisterInterface(ros2Node); RegisterInterface(ros2Node); + RegisterInterface(ros2Node); + if (!isAppEditor) + { + // services related to world loading and unloading are available only in GameLauncher + // prevent creation of those service handlers + RegisterInterface(ros2Node); + RegisterInterface(ros2Node); + RegisterInterface(ros2Node); + } } void ROS2SimulationInterfacesSystemComponent::Deactivate() diff --git a/Gems/SimulationInterfaces/Code/Source/Clients/SimulationEntitiesManager.cpp b/Gems/SimulationInterfaces/Code/Source/Clients/SimulationEntitiesManager.cpp index 7638293e2f..b3265012d6 100644 --- a/Gems/SimulationInterfaces/Code/Source/Clients/SimulationEntitiesManager.cpp +++ b/Gems/SimulationInterfaces/Code/Source/Clients/SimulationEntitiesManager.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "CommonUtilities.h" #include @@ -18,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -88,12 +90,14 @@ namespace SimulationInterfaces void SimulationEntitiesManager::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required) { required.push_back(AZ_CRC_CE("AssetCatalogService")); + required.push_back(AZ_CRC_CE("SimulationManagerService")); required.push_back(AZ_CRC_CE("SimulationFeaturesAggregator")); } void SimulationEntitiesManager::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent) { dependent.push_back(AZ_CRC_CE("PhysicsService")); + dependent.push_back(AZ_CRC_CE("SimulationManagerService")); dependent.push_back(AZ_CRC_CE("SimulationFeaturesAggregator")); } @@ -327,6 +331,11 @@ namespace SimulationInterfaces AZ::Outcome SimulationEntitiesManager::GetEntities(const EntityFilters& filter) { + if (auto outcome = IsWorldLoaded(); !outcome.IsSuccess()) + { + return AZ::Failure(outcome.GetError()); + } + if (!filter.m_tagsFilter.m_tags.empty()) { AZ_Warning("SimulationInterfaces", false, "Tags filter is not implemented yet"); @@ -400,6 +409,11 @@ namespace SimulationInterfaces AZ::Outcome SimulationEntitiesManager::GetEntityState(const AZStd::string& name) { + if (auto outcome = IsWorldLoaded(); !outcome.IsSuccess()) + { + return AZ::Failure(outcome.GetError()); + } + const auto findIt = m_simulatedEntityToEntityIdMap.find(name); if (findIt == m_simulatedEntityToEntityIdMap.end()) { @@ -427,6 +441,11 @@ namespace SimulationInterfaces AZ::Outcome SimulationEntitiesManager::GetEntitiesStates(const EntityFilters& filter) { + if (auto outcome = IsWorldLoaded(); !outcome.IsSuccess()) + { + return AZ::Failure(outcome.GetError()); + } + if (!filter.m_tagsFilter.m_tags.empty()) { AZ_Warning("SimulationInterfaces", false, "Tags filter is not implemented yet"); @@ -453,6 +472,11 @@ namespace SimulationInterfaces AZ::Outcome SimulationEntitiesManager::SetEntityState(const AZStd::string& name, const EntityState& state) { + if (auto outcome = IsWorldLoaded(); !outcome.IsSuccess()) + { + return AZ::Failure(outcome.GetError()); + } + const auto findIt = m_simulatedEntityToEntityIdMap.find(name); if (findIt == m_simulatedEntityToEntityIdMap.end()) { @@ -524,6 +548,12 @@ namespace SimulationInterfaces void SimulationEntitiesManager::DeleteEntity(const AZStd::string& name, DeletionCompletedCb completedCb) { + if (auto outcome = IsWorldLoaded(); !outcome.IsSuccess()) + { + completedCb(AZ::Failure(outcome.GetError())); + return; + } + const auto findIt = m_simulatedEntityToEntityIdMap.find(name); if (findIt == m_simulatedEntityToEntityIdMap.end()) { @@ -570,6 +600,12 @@ namespace SimulationInterfaces void SimulationEntitiesManager::DeleteAllEntities(DeletionCompletedCb completedCb) { + if (auto outcome = IsWorldLoaded(); !outcome.IsSuccess()) + { + completedCb(AZ::Failure(outcome.GetError())); + return; + } + if (m_spawnedTickets.empty()) { // early return for empty scene @@ -627,6 +663,11 @@ namespace SimulationInterfaces const bool allowRename, SpawnCompletedCb completedCb) { + if (auto outcome = IsWorldLoaded(); !outcome.IsSuccess()) + { + completedCb(AZ::Failure(outcome.GetError())); + return; + } if (!allowRename) { // If API user does not allow renaming, check if name is unique @@ -775,8 +816,13 @@ namespace SimulationInterfaces return newName; } - void SimulationEntitiesManager::ResetAllEntitiesToInitialState() + AZ::Outcome SimulationEntitiesManager::ResetAllEntitiesToInitialState() { + if (auto outcome = IsWorldLoaded(); !outcome.IsSuccess()) + { + return AZ::Failure(outcome.GetError()); + } + for (const auto& [entityId, initialState] : m_entityIdToInitialState) { AZStd::vector entityAndDescendants; @@ -784,10 +830,24 @@ namespace SimulationInterfaces SetEntitiesState(entityAndDescendants, initialState); } + return AZ::Success(); } void SimulationEntitiesManager::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) { m_unconfiguredScenesHandles = RegisterNewSimulatedBodies(m_unconfiguredScenesHandles); } + + AZ::Outcome SimulationEntitiesManager::IsWorldLoaded() + { + bool canOperate = false; + SimulationManagerRequestBus::BroadcastResult(canOperate, &SimulationManagerRequests::EntitiesOperationsPossible); + if (!canOperate) + { + return AZ::Failure(FailedResult( + simulation_interfaces::msg::Result::RESULT_OPERATION_FAILED, + "Simulator needs to have loaded world to allow entities manipulation")); + } + return AZ::Success(); + } } // namespace SimulationInterfaces diff --git a/Gems/SimulationInterfaces/Code/Source/Clients/SimulationEntitiesManager.h b/Gems/SimulationInterfaces/Code/Source/Clients/SimulationEntitiesManager.h index 215845df66..a00026cb86 100644 --- a/Gems/SimulationInterfaces/Code/Source/Clients/SimulationEntitiesManager.h +++ b/Gems/SimulationInterfaces/Code/Source/Clients/SimulationEntitiesManager.h @@ -8,6 +8,8 @@ #pragma once +#include "AzCore/Outcome/Outcome.h" +#include "SimulationInterfaces/Result.h" #include #include #include @@ -55,7 +57,7 @@ namespace SimulationInterfaces const AZ::Transform& initialPose, const bool allowRename, SpawnCompletedCb completedCb) override; - void ResetAllEntitiesToInitialState() override; + AZ::Outcome ResetAllEntitiesToInitialState() override; // AZ::TickBus::Handler interface implementation void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; @@ -88,6 +90,9 @@ namespace SimulationInterfaces //! Set the state of the entity and their descendants. void SetEntitiesState(const AZStd::vector& entityAndDescendants, const EntityState& state); + // Helper method to check if world is loaded + AZ::Outcome IsWorldLoaded(); + AzPhysics::SceneEvents::OnSimulationBodyAdded::Handler m_simulationBodyAddedHandler; AzPhysics::SceneEvents::OnSimulationBodyRemoved::Handler m_simulationBodyRemovedHandler; diff --git a/Gems/SimulationInterfaces/Code/Source/Clients/SimulationManager.cpp b/Gems/SimulationInterfaces/Code/Source/Clients/SimulationManager.cpp index 67e2f3483b..a5d123250e 100644 --- a/Gems/SimulationInterfaces/Code/Source/Clients/SimulationManager.cpp +++ b/Gems/SimulationInterfaces/Code/Source/Clients/SimulationManager.cpp @@ -7,9 +7,12 @@ */ #include "SimulationManager.h" +#include "SimulationInterfaces/LevelManagerRequestBus.h" #include "SimulationInterfaces/SimulationMangerRequestBus.h" - +#include "SimulationInterfaces/WorldResource.h" #include +#include +#include #include #include #include @@ -17,7 +20,10 @@ #include #include #include +#include +#include #include +#include namespace SimulationInterfaces { @@ -61,7 +67,9 @@ namespace SimulationInterfaces { simulation_interfaces::msg::SimulationState::STATE_PAUSED, "STATE_PAUSED" }, { simulation_interfaces::msg::SimulationState::STATE_PLAYING, "STATE_PLAYING" }, { simulation_interfaces::msg::SimulationState::STATE_QUITTING, "STATE_QUITTING" }, - { simulation_interfaces::msg::SimulationState::STATE_STOPPED, "STATE_STOPPED" } + { simulation_interfaces::msg::SimulationState::STATE_STOPPED, "STATE_STOPPED" }, + { simulation_interfaces::msg::SimulationState::STATE_NO_WORLD, "STATE_NO_WORLD" }, + { simulation_interfaces::msg::SimulationState::STATE_LOADING_WORLD, "STATE_LOADING_WORLD" } }; constexpr AZStd::string_view PrintStateName = "/SimulationInterfaces/PrintStateNameInGui"; @@ -149,11 +157,13 @@ namespace SimulationInterfaces { required.push_back(AZ_CRC_CE("PhysicsService")); required.push_back(AZ_CRC_CE("SimulationFeaturesAggregator")); + required.push_back(AZ_CRC_CE("LevelManagerService")); } void SimulationManager::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent) { dependent.push_back(AZ_CRC_CE("SimulationFeaturesAggregator")); + dependent.push_back(AZ_CRC_CE("LevelManagerService")); dependent.push_back(AZ_CRC_CE("DebugDrawTextService")); } @@ -190,7 +200,6 @@ namespace SimulationInterfaces void SimulationManager::Activate() { - AzFramework::LevelSystemLifecycleNotificationBus::Handler::BusDisconnect(); SimulationManagerRequestBus::Handler::BusConnect(); SimulationFeaturesAggregatorRequestBus::Broadcast( &SimulationFeaturesAggregatorRequests::AddSimulationFeatures, @@ -210,12 +219,6 @@ namespace SimulationInterfaces AZ::TickBus::Handler::BusConnect(); } - AZ::SystemTickBus::QueueFunction( - [this]() - { - InitializeSimulationState(); - }); - // Query registry for keyboard transition keys if (const auto stoppedToPlayingKey = GetKeyboardTransitionKey(KeyboardTransitionStoppedToPlaying)) { @@ -241,6 +244,26 @@ namespace SimulationInterfaces simulation_interfaces::msg::SimulationState::STATE_PAUSED); } InputChannelEventListener::BusConnect(); + + // wait one tick to allow all system to start to ensure correct bus calls related to setting simulation state + AZ::SystemTickBus::QueueFunction( + [this]() + { + // check if level is loaded + AZ::Outcome getCurrentWorldOutcome; + LevelManagerRequestBus::BroadcastResult(getCurrentWorldOutcome, &LevelManagerRequestBus::Events::GetCurrentWorld); + + if (!getCurrentWorldOutcome.IsSuccess() && + getCurrentWorldOutcome.GetError().m_errorCode == simulation_interfaces::srv::GetCurrentWorld::Response::NO_WORLD_LOADED) + { + SetSimulationState(simulation_interfaces::msg::SimulationState::STATE_NO_WORLD); + } + else if (getCurrentWorldOutcome.IsSuccess()) + { + m_startedWithLoadedLevel = true; + InitializeSimulationState(); + } + }); } void SimulationManager::Deactivate() @@ -326,36 +349,44 @@ namespace SimulationInterfaces SetSimulationPaused(false); } - void SimulationManager::ReloadLevel(SimulationManagerRequests::ReloadLevelCallback completionCallback) + AZ::Outcome SimulationManager::ResetSimulation(ReloadLevelCallback completionCallback) { - AzFramework::LevelSystemLifecycleNotificationBus::Handler::BusConnect(); + // check if simulation has loaded level - if not it is impossible to restart it since there is no default state + if (m_simulationState == simulation_interfaces::msg::SimulationState::STATE_LOADING_WORLD || + m_simulationState == simulation_interfaces::msg::SimulationState::STATE_NO_WORLD) + { + return AZ::Failure( + FailedResult(simulation_interfaces::msg::Result::RESULT_OPERATION_FAILED, "Cannot reset simulation without loaded level")); + } m_reloadLevelCallback = completionCallback; - // We need to delete all entities before reloading the level - DeletionCompletedCb deleteAllCompletion = [](const AZ::Outcome& result) + DeletionCompletedCb deleteAllCompletion = + [startedWithLevel = m_startedWithLoadedLevel, completionCallback](const AZ::Outcome& result) { - AZ_Trace("SimulationManager", "Delete all entities completed: %s, reload level", result.IsSuccess() ? "true" : "false"); - const char* levelName = AZ::Interface::Get()->GetCurrentLevelName(); - AzFramework::ConsoleRequestBus::Broadcast(&AzFramework::ConsoleRequests::ExecuteConsoleCommand, "UnloadLevel"); - AZStd::string command = AZStd::string::format("LoadLevel %s", levelName); - AzFramework::ConsoleRequestBus::Broadcast(&AzFramework::ConsoleRequests::ExecuteConsoleCommand, command.c_str()); + AZ_Info("SimulationManager", "Delete all entities completed: %s, reload level", result.IsSuccess() ? "true" : "false"); + // queue required to allow all resources related to removed spawnables to be released, especially those related to level.pak + AZ::SystemTickBus::QueueFunction( + [startedWithLevel, completionCallback]() + { + // call level manager to reload the level + if (startedWithLevel) + { + LevelManagerRequestBus::Broadcast(&LevelManagerRequests::ReloadLevel); + } + else + { + LevelManagerRequestBus::Broadcast(&LevelManagerRequests::UnloadWorld); + if (completionCallback) + { + completionCallback(); + } + } + }); }; // delete spawned entities SimulationEntityManagerRequestBus::Broadcast(&SimulationEntityManagerRequests::DeleteAllEntities, deleteAllCompletion); - } - - void SimulationManager::OnLoadingComplete(const char* levelName) - { - AZ_Printf("SimulationManager", "Level loading started: %s", levelName); - if (m_reloadLevelCallback) - { - m_reloadLevelCallback(); - m_reloadLevelCallback = nullptr; - } - // reset of the simulation, assign the same state as at the beginning - InitializeSimulationState(); - AzFramework::LevelSystemLifecycleNotificationBus::Handler::BusDisconnect(); + return AZ::Success(); } SimulationState SimulationManager::GetSimulationState() const @@ -397,12 +428,27 @@ namespace SimulationInterfaces { case simulation_interfaces::msg::SimulationState::STATE_STOPPED: { - SimulationManagerRequests::ReloadLevelCallback cb = []() + if (m_reloadLevelCallback) { - SimulationInterfaces::SimulationManagerRequestBus::Broadcast( - &SimulationInterfaces::SimulationManagerRequests::SetSimulationPaused, true); - }; - ReloadLevel(cb); + m_reloadLevelCallback(); + m_reloadLevelCallback = nullptr; + } + if (m_levelLoaded) + { + InitializeSimulationState(); + m_levelLoaded = false; + } + else // transition from other state than load world + { + DeletionCompletedCb deleteAllCompletion = [this](const AZ::Outcome& result) + { + AZ_Info("SimulationManager", "Delete all entities completed: %s", result.IsSuccess() ? "true" : "false"); + InitializeSimulationState(); + }; + // delete spawned entities + SimulationEntityManagerRequestBus::Broadcast(&SimulationEntityManagerRequests::DeleteAllEntities, deleteAllCompletion); + } + break; } case simulation_interfaces::msg::SimulationState::STATE_PLAYING: @@ -428,6 +474,19 @@ namespace SimulationInterfaces }); break; } + case simulation_interfaces::msg::SimulationState::STATE_NO_WORLD: + { + // restore initial state for variables + m_isSimulationPaused = false; + m_levelLoaded = false; + m_numberOfPhysicsSteps = 0; + break; + } + case simulation_interfaces::msg::SimulationState::STATE_LOADING_WORLD: + { + m_levelLoaded = true; + break; + } default: { return AZ::Failure(FailedResult( @@ -439,6 +498,13 @@ namespace SimulationInterfaces return AZ::Success(); } + bool SimulationManager::EntitiesOperationsPossible() + { + return ( + m_simulationState != simulation_interfaces::msg::SimulationState::STATE_LOADING_WORLD && + m_simulationState != simulation_interfaces::msg::SimulationState::STATE_NO_WORLD); + } + bool SimulationManager::IsTransitionForbiddenInEditor(SimulationState requestedState) { // in the Editor we cannot reload level, so going to STOPPED state is forbidden, we cannot quit the editor so going to QUITTING diff --git a/Gems/SimulationInterfaces/Code/Source/Clients/SimulationManager.h b/Gems/SimulationInterfaces/Code/Source/Clients/SimulationManager.h index 31064d959f..e768fe830c 100644 --- a/Gems/SimulationInterfaces/Code/Source/Clients/SimulationManager.h +++ b/Gems/SimulationInterfaces/Code/Source/Clients/SimulationManager.h @@ -12,8 +12,8 @@ #include #include #include +#include #include -#include #include #include #include @@ -21,6 +21,7 @@ #include #include #include +#include #include namespace SimulationInterfaces @@ -34,7 +35,6 @@ namespace SimulationInterfaces class SimulationManager : public AZ::Component , protected SimulationManagerRequestBus::Handler - , protected AzFramework::LevelSystemLifecycleNotificationBus::Handler , protected AZ::TickBus::Handler , protected AzFramework::InputChannelEventListener { @@ -56,19 +56,16 @@ namespace SimulationInterfaces void Deactivate() override; private: - // SimulationManagerRequestBus interface implementation + AZ::Outcome ResetSimulation(ReloadLevelCallback completionCallback) override; void SetSimulationPaused(bool paused) override; void StepSimulation(AZ::u64 steps) override; bool IsSimulationPaused() const override; void CancelStepSimulation() override; bool IsSimulationStepsActive() const override; - void ReloadLevel(SimulationManagerRequests::ReloadLevelCallback completionCallback) override; SimulationState GetSimulationState() const override; AZ::Outcome SetSimulationState(SimulationState stateToSet) override; - - // LevelSystemLifecycleNotificationBus interface implementation - void OnLoadingComplete(const char* levelName) override; + bool EntitiesOperationsPossible() override; // AZ::TickBus::Handler void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; @@ -86,6 +83,7 @@ namespace SimulationInterfaces void UnregisterAllTransitionKeys(); bool m_isSimulationPaused = false; + bool m_startedWithLoadedLevel = false; uint64_t m_numberOfPhysicsSteps = 0; AzPhysics::SceneEvents::OnSceneSimulationFinishHandler m_simulationFinishEvent; @@ -99,14 +97,18 @@ namespace SimulationInterfaces bool IsTransitionForbidden(SimulationState requestedState); // forbidden transition between state, first is current state, second is desire state - const AZStd::array, 4> m_forbiddenStatesTransitions{ { - { simulation_interfaces::msg::SimulationState::STATE_STOPPED, simulation_interfaces::msg::SimulationState::STATE_PAUSED }, - { simulation_interfaces::msg::SimulationState::STATE_QUITTING, simulation_interfaces::msg::SimulationState::STATE_STOPPED }, - { simulation_interfaces::msg::SimulationState::STATE_QUITTING, simulation_interfaces::msg::SimulationState::STATE_PLAYING }, - { simulation_interfaces::msg::SimulationState::STATE_QUITTING, simulation_interfaces::msg::SimulationState::STATE_PAUSED }, - } }; + const AZStd::array, 7> m_forbiddenStatesTransitions{ + { { simulation_interfaces::msg::SimulationState::STATE_STOPPED, simulation_interfaces::msg::SimulationState::STATE_PAUSED }, + { simulation_interfaces::msg::SimulationState::STATE_QUITTING, simulation_interfaces::msg::SimulationState::STATE_STOPPED }, + { simulation_interfaces::msg::SimulationState::STATE_QUITTING, simulation_interfaces::msg::SimulationState::STATE_PLAYING }, + { simulation_interfaces::msg::SimulationState::STATE_QUITTING, simulation_interfaces::msg::SimulationState::STATE_PAUSED }, + { simulation_interfaces::msg::SimulationState::STATE_NO_WORLD, simulation_interfaces::msg::SimulationState::STATE_STOPPED }, + { simulation_interfaces::msg::SimulationState::STATE_NO_WORLD, simulation_interfaces::msg::SimulationState::STATE_PLAYING }, + { simulation_interfaces::msg::SimulationState::STATE_NO_WORLD, simulation_interfaces::msg::SimulationState::STATE_PAUSED } } + }; //! Map of keyboard transitions - defined in registry key AZStd::unordered_map m_keyboardTransitions; + bool m_levelLoaded = false; }; } // namespace SimulationInterfaces diff --git a/Gems/SimulationInterfaces/Code/Source/Services/GetAvailableWorldsServiceHandler.cpp b/Gems/SimulationInterfaces/Code/Source/Services/GetAvailableWorldsServiceHandler.cpp new file mode 100644 index 0000000000..a35d9c111f --- /dev/null +++ b/Gems/SimulationInterfaces/Code/Source/Services/GetAvailableWorldsServiceHandler.cpp @@ -0,0 +1,77 @@ +/* + * 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 "GetAvailableWorldsServiceHandler.h" +#include "SimulationInterfaces/TagFilter.h" +#include "SimulationInterfaces/WorldResource.h" +#include +#include +#include +#include +#include +#include "Utils/Utils.h" + +namespace ROS2SimulationInterfaces +{ + namespace + { + [[nodiscard]] SimulationInterfaces::GetWorldsRequest ConvertROS2Request( + const GetAvailableWorldsServiceHandler::Request& ros2Request) + { + SimulationInterfaces::GetWorldsRequest request; + request.continueOnError = ros2Request.continue_on_error; + request.offlineOnly = ros2Request.offline_only; + for (const auto& source : ros2Request.additional_sources) + { + request.additionalSources.emplace_back(source.c_str()); + } + request.filter.m_mode = ros2Request.filter.filter_mode; + for (const auto& tag : ros2Request.filter.tags) + { + request.filter.m_tags.insert(tag.c_str()); + } + return request; + } + } // namespace + + AZStd::unordered_set GetAvailableWorldsServiceHandler::GetProvidedFeatures() + { + return AZStd::unordered_set{ SimulationFeatures::AVAILABLE_WORLDS }; + } + + AZStd::optional GetAvailableWorldsServiceHandler::HandleServiceRequest( + const std::shared_ptr header, const Request& request) + { + Response response; + AZ::Outcome availableWorlds; + const SimulationInterfaces::GetWorldsRequest getWorldsRequest = ConvertROS2Request(request); + + SimulationInterfaces::LevelManagerRequestBus::BroadcastResult( + availableWorlds, &SimulationInterfaces::LevelManagerRequests::GetAvailableWorlds, getWorldsRequest); + + if (!availableWorlds.IsSuccess()) + { + response.result.result = availableWorlds.GetError().m_errorCode; + response.result.error_message = availableWorlds.GetError().m_errorString.c_str(); + } + else + { + response.result.result = simulation_interfaces::msg::Result::RESULT_OK; + const auto& worldsList = availableWorlds.GetValue(); + AZStd::transform( + worldsList.begin(), + worldsList.end(), + AZStd::back_inserter(response.worlds), + [](const SimulationInterfaces::WorldResource& resource) + { + return Utils::ConvertToRos2WorldResource(resource); + }); + } + return response; + } +} // namespace ROS2SimulationInterfaces diff --git a/Gems/SimulationInterfaces/Code/Source/Services/GetAvailableWorldsServiceHandler.h b/Gems/SimulationInterfaces/Code/Source/Services/GetAvailableWorldsServiceHandler.h new file mode 100644 index 0000000000..4c25c92c08 --- /dev/null +++ b/Gems/SimulationInterfaces/Code/Source/Services/GetAvailableWorldsServiceHandler.h @@ -0,0 +1,35 @@ +/* + * 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 + * + */ + +#pragma once + +#include "ROS2ServiceBase.h" +#include +#include + +namespace ROS2SimulationInterfaces +{ + + class GetAvailableWorldsServiceHandler : public ROS2ServiceBase + { + public: + AZStd::string_view GetTypeName() const override + { + return "GetAvailableWorlds"; + } + + AZStd::string_view GetDefaultName() const override + { + return "get_available_worlds"; + } + AZStd::unordered_set GetProvidedFeatures() override; + + AZStd::optional HandleServiceRequest(const std::shared_ptr header, const Request& request) override; + }; + +} // namespace ROS2SimulationInterfaces diff --git a/Gems/SimulationInterfaces/Code/Source/Services/GetCurrentWorldServiceHandler.cpp b/Gems/SimulationInterfaces/Code/Source/Services/GetCurrentWorldServiceHandler.cpp new file mode 100644 index 0000000000..69733ee22d --- /dev/null +++ b/Gems/SimulationInterfaces/Code/Source/Services/GetCurrentWorldServiceHandler.cpp @@ -0,0 +1,44 @@ +/* + * 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 "GetCurrentWorldServiceHandler.h" +#include "Utils/Utils.h" +#include +#include +#include +#include + +namespace ROS2SimulationInterfaces +{ + + AZStd::unordered_set GetCurrentWorldServiceHandler::GetProvidedFeatures() + { + return AZStd::unordered_set{ SimulationFeatures::WORLD_INFO_GETTING }; + } + + AZStd::optional GetCurrentWorldServiceHandler::HandleServiceRequest( + const std::shared_ptr header, const Request& request) + { + AZ::Outcome currentWorld; + + SimulationInterfaces::LevelManagerRequestBus::BroadcastResult( + currentWorld, &SimulationInterfaces::LevelManagerRequests::GetCurrentWorld); + Response response; + if (!currentWorld.IsSuccess()) + { + response.result.result = currentWorld.GetError().m_errorCode; + response.result.error_message = currentWorld.GetError().m_errorString.c_str(); + } + else + { + response.result.result = simulation_interfaces::msg::Result::RESULT_OK; + response.world = Utils::ConvertToRos2WorldResource(currentWorld.GetValue()); + } + return response; + } +} // namespace ROS2SimulationInterfaces diff --git a/Gems/SimulationInterfaces/Code/Source/Services/GetCurrentWorldServiceHandler.h b/Gems/SimulationInterfaces/Code/Source/Services/GetCurrentWorldServiceHandler.h new file mode 100644 index 0000000000..037bea9bbf --- /dev/null +++ b/Gems/SimulationInterfaces/Code/Source/Services/GetCurrentWorldServiceHandler.h @@ -0,0 +1,35 @@ +/* + * 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 + * + */ + +#pragma once + +#include "ROS2ServiceBase.h" +#include +#include + +namespace ROS2SimulationInterfaces +{ + + class GetCurrentWorldServiceHandler : public ROS2ServiceBase + { + public: + AZStd::string_view GetTypeName() const override + { + return "GetCurrentWorld"; + } + + AZStd::string_view GetDefaultName() const override + { + return "get_current_world"; + } + AZStd::unordered_set GetProvidedFeatures() override; + + AZStd::optional HandleServiceRequest(const std::shared_ptr header, const Request& request) override; + }; + +} // namespace ROS2SimulationInterfaces diff --git a/Gems/SimulationInterfaces/Code/Source/Services/LoadWorldServiceHandler.cpp b/Gems/SimulationInterfaces/Code/Source/Services/LoadWorldServiceHandler.cpp new file mode 100644 index 0000000000..0d4e9699c8 --- /dev/null +++ b/Gems/SimulationInterfaces/Code/Source/Services/LoadWorldServiceHandler.cpp @@ -0,0 +1,55 @@ +/* + * 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 "LoadWorldServiceHandler.h" +#include "SimulationInterfaces/WorldResource.h" +#include "Utils/Utils.h" +#include +#include + +namespace ROS2SimulationInterfaces +{ + namespace + { + [[nodiscard]] SimulationInterfaces::LoadWorldRequest ConvertROS2Request(const LoadWorldServiceHandler::Request& ros2Request) + { + SimulationInterfaces::LoadWorldRequest request; + request.failOnUnsupportedElement = ros2Request.fail_on_unsupported_element; + request.ignoreMissingOrUnsupportedAssets = ros2Request.ignore_missing_or_unsupported_assets; + request.levelResource.m_uri = ros2Request.uri.c_str(); + request.levelResource.m_resourceString = ros2Request.resource_string.c_str(); + return request; + } + } // namespace + + AZStd::unordered_set LoadWorldServiceHandler::GetProvidedFeatures() + { + return AZStd::unordered_set{ SimulationFeatures::WORLD_LOADING }; + } + + AZStd::optional LoadWorldServiceHandler::HandleServiceRequest( + const std::shared_ptr header, const Request& request) + { + AZ::Outcome loadedWorld; + SimulationInterfaces::LoadWorldRequest loadWorldRequest = ConvertROS2Request(request); + SimulationInterfaces::LevelManagerRequestBus::BroadcastResult( + loadedWorld, &SimulationInterfaces::LevelManagerRequests::LoadWorld, loadWorldRequest); + Response response; + if (!loadedWorld.IsSuccess()) + { + response.result.result = loadedWorld.GetError().m_errorCode; + response.result.error_message = loadedWorld.GetError().m_errorString.c_str(); + } + else + { + response.result.result = simulation_interfaces::msg::Result::RESULT_OK; + response.world = Utils::ConvertToRos2WorldResource(loadedWorld.GetValue()); + } + return response; + } +} // namespace ROS2SimulationInterfaces diff --git a/Gems/SimulationInterfaces/Code/Source/Services/LoadWorldServiceHandler.h b/Gems/SimulationInterfaces/Code/Source/Services/LoadWorldServiceHandler.h new file mode 100644 index 0000000000..4209ab50bf --- /dev/null +++ b/Gems/SimulationInterfaces/Code/Source/Services/LoadWorldServiceHandler.h @@ -0,0 +1,35 @@ +/* + * 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 + * + */ + +#pragma once + +#include "ROS2ServiceBase.h" +#include +#include + +namespace ROS2SimulationInterfaces +{ + + class LoadWorldServiceHandler : public ROS2ServiceBase + { + public: + AZStd::string_view GetTypeName() const override + { + return "LoadWorld"; + } + + AZStd::string_view GetDefaultName() const override + { + return "load_world"; + } + AZStd::unordered_set GetProvidedFeatures() override; + + AZStd::optional HandleServiceRequest(const std::shared_ptr header, const Request& request) override; + }; + +} // namespace ROS2SimulationInterfaces diff --git a/Gems/SimulationInterfaces/Code/Source/Services/ResetSimulationServiceHandler.cpp b/Gems/SimulationInterfaces/Code/Source/Services/ResetSimulationServiceHandler.cpp index 50d0cea020..fe8da61b5d 100644 --- a/Gems/SimulationInterfaces/Code/Source/Services/ResetSimulationServiceHandler.cpp +++ b/Gems/SimulationInterfaces/Code/Source/Services/ResetSimulationServiceHandler.cpp @@ -8,18 +8,21 @@ #include "ResetSimulationServiceHandler.h" #include +#include #include #include #include +#include +#include +#include #include #include #include #include -#include +#include namespace ROS2SimulationInterfaces { - AZStd::unordered_set ResetSimulationServiceHandler::GetProvidedFeatures() { return AZStd::unordered_set{ SimulationFeatures::SIMULATION_RESET, @@ -34,9 +37,18 @@ namespace ROS2SimulationInterfaces if (request.scope == Request::SCOPE_STATE) { Response response; - SimulationInterfaces::SimulationEntityManagerRequestBus::Broadcast( - &SimulationInterfaces::SimulationEntityManagerRequests::ResetAllEntitiesToInitialState); - response.result.result = simulation_interfaces::msg::Result::RESULT_OK; + AZ::Outcome resetingOutcome; + SimulationInterfaces::SimulationEntityManagerRequestBus::BroadcastResult( + resetingOutcome, &SimulationInterfaces::SimulationEntityManagerRequests::ResetAllEntitiesToInitialState); + if (resetingOutcome.IsSuccess()) + { + response.result.result = simulation_interfaces::msg::Result::RESULT_OK; + } + else + { + response.result.result = resetingOutcome.GetError().m_errorCode; + response.result.error_message = resetingOutcome.GetError().m_errorString.c_str(); + } return response; } @@ -107,8 +119,16 @@ namespace ROS2SimulationInterfaces response.result.result = simulation_interfaces::msg::Result::RESULT_OK; SendResponse(response); }; - SimulationInterfaces::SimulationManagerRequestBus::Broadcast( - &SimulationInterfaces::SimulationManagerRequests::ReloadLevel, levelReloadCompletion); + AZ::Outcome restartOutcome; + SimulationInterfaces::SimulationManagerRequestBus::BroadcastResult( + restartOutcome, &SimulationInterfaces::SimulationManagerRequests::ResetSimulation, levelReloadCompletion); + if (!restartOutcome.IsSuccess()) + { + Response invalidResponse; + invalidResponse.result.result = restartOutcome.GetError().m_errorCode; + invalidResponse.result.error_message = restartOutcome.GetError().m_errorString.c_str(); + return invalidResponse; + } return AZStd::nullopt; } diff --git a/Gems/SimulationInterfaces/Code/Source/Services/ResetSimulationServiceHandler.h b/Gems/SimulationInterfaces/Code/Source/Services/ResetSimulationServiceHandler.h index c3de46c9f8..81a72443d6 100644 --- a/Gems/SimulationInterfaces/Code/Source/Services/ResetSimulationServiceHandler.h +++ b/Gems/SimulationInterfaces/Code/Source/Services/ResetSimulationServiceHandler.h @@ -26,6 +26,7 @@ namespace ROS2SimulationInterfaces { return "reset_simulation"; } + AZStd::unordered_set GetProvidedFeatures() override; AZStd::optional HandleServiceRequest(const std::shared_ptr header, const Request& request) override; diff --git a/Gems/SimulationInterfaces/Code/Source/Services/UnloadWorldServiceHandler.cpp b/Gems/SimulationInterfaces/Code/Source/Services/UnloadWorldServiceHandler.cpp new file mode 100644 index 0000000000..1a135fb3d3 --- /dev/null +++ b/Gems/SimulationInterfaces/Code/Source/Services/UnloadWorldServiceHandler.cpp @@ -0,0 +1,40 @@ +/* + * 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 "UnloadWorldServiceHandler.h" +#include +#include + +namespace ROS2SimulationInterfaces +{ + + AZStd::unordered_set UnloadWorldServiceHandler::GetProvidedFeatures() + { + return AZStd::unordered_set{ SimulationFeatures::WORLD_UNLOADING }; + } + + AZStd::optional UnloadWorldServiceHandler::HandleServiceRequest( + const std::shared_ptr header, const Request& request) + { + AZ::Outcome unloadWorld; + + SimulationInterfaces::LevelManagerRequestBus::BroadcastResult( + unloadWorld, &SimulationInterfaces::LevelManagerRequests::UnloadWorld); + Response response; + if (!unloadWorld.IsSuccess()) + { + response.result.result = unloadWorld.GetError().m_errorCode; + response.result.error_message = unloadWorld.GetError().m_errorString.c_str(); + } + else + { + response.result.result = simulation_interfaces::msg::Result::RESULT_OK; + } + return response; + } +} // namespace ROS2SimulationInterfaces diff --git a/Gems/SimulationInterfaces/Code/Source/Services/UnloadWorldServiceHandler.h b/Gems/SimulationInterfaces/Code/Source/Services/UnloadWorldServiceHandler.h new file mode 100644 index 0000000000..e72fc73dd1 --- /dev/null +++ b/Gems/SimulationInterfaces/Code/Source/Services/UnloadWorldServiceHandler.h @@ -0,0 +1,35 @@ +/* + * 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 + * + */ + +#pragma once + +#include "ROS2ServiceBase.h" +#include +#include + +namespace ROS2SimulationInterfaces +{ + + class UnloadWorldServiceHandler : public ROS2ServiceBase + { + public: + AZStd::string_view GetTypeName() const override + { + return "UnloadWorld"; + } + + AZStd::string_view GetDefaultName() const override + { + return "unload_world"; + } + AZStd::unordered_set GetProvidedFeatures() override; + + AZStd::optional HandleServiceRequest(const std::shared_ptr header, const Request& request) override; + }; + +} // namespace ROS2SimulationInterfaces diff --git a/Gems/SimulationInterfaces/Code/Source/SimulationInterfacesModuleInterface.cpp b/Gems/SimulationInterfaces/Code/Source/SimulationInterfacesModuleInterface.cpp index bebf4cc4ce..6f1515ebbe 100644 --- a/Gems/SimulationInterfaces/Code/Source/SimulationInterfacesModuleInterface.cpp +++ b/Gems/SimulationInterfaces/Code/Source/SimulationInterfacesModuleInterface.cpp @@ -8,6 +8,7 @@ #include "SimulationInterfacesModuleInterface.h" #include +#include #include @@ -34,6 +35,7 @@ namespace SimulationInterfaces SimulationManager::CreateDescriptor(), SimulationFeaturesAggregator::CreateDescriptor(), NamedPoseManager::CreateDescriptor(), + LevelManager::CreateDescriptor(), ROS2SimulationInterfaces::ROS2SimulationInterfacesSystemComponent::CreateDescriptor(), NamedPoseComponent::CreateDescriptor(), }); @@ -46,6 +48,7 @@ namespace SimulationInterfaces azrtti_typeid(), azrtti_typeid(), azrtti_typeid(), + azrtti_typeid(), azrtti_typeid(), }; } diff --git a/Gems/SimulationInterfaces/Code/Source/Tools/LevelManagerEditor.cpp b/Gems/SimulationInterfaces/Code/Source/Tools/LevelManagerEditor.cpp new file mode 100644 index 0000000000..5ab16a54ac --- /dev/null +++ b/Gems/SimulationInterfaces/Code/Source/Tools/LevelManagerEditor.cpp @@ -0,0 +1,79 @@ +/* + * 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 "LevelManagerEditor.h" +#include + +#include + +namespace SimulationInterfaces +{ + AZ_COMPONENT_IMPL(LevelManagerEditor, "SimulationMangerEditor", LevelManagerEditorTypeId, BaseSystemComponent); + + void LevelManagerEditor::Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class()->Version(0); + } + } + + LevelManagerEditor::LevelManagerEditor() = default; + + LevelManagerEditor::~LevelManagerEditor() = default; + + void LevelManagerEditor::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + BaseSystemComponent::GetProvidedServices(provided); + provided.push_back(AZ_CRC_CE("LevelManagerEditorService")); + } + + void LevelManagerEditor::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + BaseSystemComponent::GetIncompatibleServices(incompatible); + incompatible.push_back(AZ_CRC_CE("LevelManagerEditorService")); + } + + void LevelManagerEditor::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required) + { + BaseSystemComponent::GetRequiredServices(required); + required.push_back(AZ_CRC_CE("SimulationFeaturesAggregatorEditorService")); + } + + void LevelManagerEditor::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + BaseSystemComponent::GetDependentServices(dependent); + dependent.push_back(AZ_CRC_CE("SimulationFeaturesAggregatorEditorService")); + } + void LevelManagerEditor::Init() + { + BaseSystemComponent::Init(); + } + + void LevelManagerEditor::Activate() + { + AzToolsFramework::EditorEvents::Bus::Handler::BusConnect(); + AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusConnect(); + } + + void LevelManagerEditor::Deactivate() + { + AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusDisconnect(); + AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect(); + } + + void LevelManagerEditor::OnStartPlayInEditorBegin() + { + BaseSystemComponent::Activate(); + } + void LevelManagerEditor::OnStopPlayInEditorBegin() + { + BaseSystemComponent::Deactivate(); + } + +} // namespace SimulationInterfaces diff --git a/Gems/SimulationInterfaces/Code/Source/Tools/LevelManagerEditor.h b/Gems/SimulationInterfaces/Code/Source/Tools/LevelManagerEditor.h new file mode 100644 index 0000000000..b06777ae5d --- /dev/null +++ b/Gems/SimulationInterfaces/Code/Source/Tools/LevelManagerEditor.h @@ -0,0 +1,51 @@ +/* + * 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 + * + */ + +#pragma once + +#include +#include + +#include + +namespace SimulationInterfaces +{ + /// System component for SimulationInterfaces editor + // This Part of the system is most not active in Editor but due to added required and dependant services it is required to exist in + // Editor + class LevelManagerEditor + : public LevelManager + , protected AzToolsFramework::EditorEvents::Bus::Handler + , private AzToolsFramework::EditorEntityContextNotificationBus::Handler + { + using BaseSystemComponent = LevelManager; + + public: + AZ_COMPONENT_DECL(LevelManagerEditor); + + static void Reflect(AZ::ReflectContext* context); + + LevelManagerEditor(); + ~LevelManagerEditor(); + + private: + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + + // AZ::Component + void Init() override; + void Activate() override; + void Deactivate() override; + + // EditorEntityContextNotificationBus + void OnStartPlayInEditorBegin() override; + void OnStopPlayInEditorBegin() override; + }; +} // namespace SimulationInterfaces diff --git a/Gems/SimulationInterfaces/Code/Source/Tools/SimulationInterfacesEditorModule.cpp b/Gems/SimulationInterfaces/Code/Source/Tools/SimulationInterfacesEditorModule.cpp index 94d2ca47a0..c8144b797b 100644 --- a/Gems/SimulationInterfaces/Code/Source/Tools/SimulationInterfacesEditorModule.cpp +++ b/Gems/SimulationInterfaces/Code/Source/Tools/SimulationInterfacesEditorModule.cpp @@ -6,6 +6,7 @@ * */ +#include "LevelManagerEditor.h" #include "NamedPoseManagerEditor.h" #include "ROS2SimulationInterfacesEditorSystemComponent.h" #include "SimulationEntitiesManagerEditor.h" @@ -31,6 +32,7 @@ namespace SimulationInterfaces SimulationManagerEditor::CreateDescriptor(), SimulationFeaturesAggregatorEditor::CreateDescriptor(), NamedPoseManagerEditor::CreateDescriptor(), + LevelManagerEditor::CreateDescriptor(), ROS2SimulationInterfaces::ROS2SimulationInterfacesEditorSystemComponent::CreateDescriptor(), NamedPoseEditorComponent::CreateDescriptor(), }); @@ -49,6 +51,7 @@ namespace SimulationInterfaces azrtti_typeid(), azrtti_typeid(), azrtti_typeid(), + azrtti_typeid(), azrtti_typeid(), }; } diff --git a/Gems/SimulationInterfaces/Code/Source/Utils/Utils.h b/Gems/SimulationInterfaces/Code/Source/Utils/Utils.h index 0a544f0148..0470275f64 100644 --- a/Gems/SimulationInterfaces/Code/Source/Utils/Utils.h +++ b/Gems/SimulationInterfaces/Code/Source/Utils/Utils.h @@ -11,7 +11,9 @@ #include #include #include +#include #include +#include namespace ROS2SimulationInterfaces::Utils { @@ -61,4 +63,23 @@ namespace ROS2SimulationInterfaces::Utils } return AZ::Success(AZStd::move(filter)); } + + inline simulation_interfaces::msg::WorldResource ConvertToRos2WorldResource(const SimulationInterfaces::WorldResource& resource) + { + simulation_interfaces::msg::WorldResource worldResource; + worldResource.name = resource.m_name.c_str(); + worldResource.description = resource.m_description.c_str(); + worldResource.world_resource.uri = resource.m_worldResource.m_uri.c_str(); + worldResource.world_resource.resource_string = resource.m_worldResource.m_resourceString.c_str(); + AZStd::transform( + resource.m_tags.begin(), + resource.m_tags.end(), + AZStd::back_inserter(worldResource.tags), + [](const AZStd::string& tag) + { + std::string stdTag(tag.c_str()); + return stdTag; + }); + return worldResource; + }; } // namespace ROS2SimulationInterfaces::Utils diff --git a/Gems/SimulationInterfaces/Code/Tests/Tools/Mocks/SimulationEntityManagerMock.h b/Gems/SimulationInterfaces/Code/Tests/Tools/Mocks/SimulationEntityManagerMock.h index 336981c295..0e6512621e 100644 --- a/Gems/SimulationInterfaces/Code/Tests/Tools/Mocks/SimulationEntityManagerMock.h +++ b/Gems/SimulationInterfaces/Code/Tests/Tools/Mocks/SimulationEntityManagerMock.h @@ -24,7 +24,7 @@ namespace UnitTest MOCK_METHOD2(DeleteEntity, void(const AZStd::string& name, DeletionCompletedCb completedCb)); MOCK_METHOD1(DeleteAllEntities, void(DeletionCompletedCb completedCb)); MOCK_METHOD0(GetSpawnables, AZ::Outcome()); - MOCK_METHOD0(ResetAllEntitiesToInitialState, void()); + MOCK_METHOD0(ResetAllEntitiesToInitialState, AZ::Outcome()); MOCK_METHOD6( SpawnEntity, void( diff --git a/Gems/SimulationInterfaces/Code/Tests/Tools/Mocks/SimulationManagerMock.h b/Gems/SimulationInterfaces/Code/Tests/Tools/Mocks/SimulationManagerMock.h index 475178de97..7ca8fbdbb1 100644 --- a/Gems/SimulationInterfaces/Code/Tests/Tools/Mocks/SimulationManagerMock.h +++ b/Gems/SimulationInterfaces/Code/Tests/Tools/Mocks/SimulationManagerMock.h @@ -18,7 +18,7 @@ namespace UnitTest SimulationInterfaces::SimulationManagerRequestBus::Handler::BusDisconnect(); } - MOCK_METHOD1(ReloadLevel, void(ReloadLevelCallback)); + MOCK_METHOD1(ResetSimulation, AZ::Outcome(ReloadLevelCallback)); MOCK_METHOD1(SetSimulationPaused, void(bool)); MOCK_METHOD1(StepSimulation, void(AZ::u64)); MOCK_METHOD(bool, IsSimulationPaused, (), (const)); @@ -26,5 +26,6 @@ namespace UnitTest MOCK_METHOD(bool, IsSimulationStepsActive, (), (const)); MOCK_METHOD(SimulationState, GetSimulationState, (), (const)); MOCK_METHOD1(SetSimulationState, AZ::Outcome(SimulationState)); + MOCK_METHOD0(EntitiesOperationsPossible, bool()); }; } // namespace UnitTest diff --git a/Gems/SimulationInterfaces/Code/Tests/Tools/TestFixture.cpp b/Gems/SimulationInterfaces/Code/Tests/Tools/TestFixture.cpp index 4dd1772bd9..beb4bcb073 100644 --- a/Gems/SimulationInterfaces/Code/Tests/Tools/TestFixture.cpp +++ b/Gems/SimulationInterfaces/Code/Tests/Tools/TestFixture.cpp @@ -8,7 +8,8 @@ */ #include "TestFixture.h" -#include "Clients/SimulationEntitiesManager.h" +#include +#include #include namespace UnitTest { @@ -20,17 +21,16 @@ namespace UnitTest AddActiveGems(requiredGems); AddDynamicModulePaths({ "PhysX5.Gem" }); AddDynamicModulePaths({ "LmbrCentral" }); - AddComponentDescriptors({ - SimulationInterfaces::SimulationEntitiesManager::CreateDescriptor(), - }); - AddRequiredComponents({ SimulationInterfaces::SimulationEntitiesManager::TYPEINFO_Uuid() }); + AddComponentDescriptors( + AZStd::initializer_list{ SimulationInterfaces::SimulationEntitiesManager::CreateDescriptor(), + SimulationInterfaces::SimulationManager::CreateDescriptor() }); + AddRequiredComponents( + { SimulationInterfaces::SimulationEntitiesManager::TYPEINFO_Uuid(), SimulationInterfaces::SimulationManager::TYPEINFO_Uuid() }); } void SimulationInterfaceTestEnvironment::PostSystemEntityActivate() { AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); - - // load asset catalog } AZ::ComponentApplication* SimulationInterfaceTestEnvironment::CreateApplicationInstance() diff --git a/Gems/SimulationInterfaces/Code/simulationinterfaces_api_files.cmake b/Gems/SimulationInterfaces/Code/simulationinterfaces_api_files.cmake index 036a5b3821..e842c43a8f 100644 --- a/Gems/SimulationInterfaces/Code/simulationinterfaces_api_files.cmake +++ b/Gems/SimulationInterfaces/Code/simulationinterfaces_api_files.cmake @@ -9,10 +9,13 @@ set(FILES Include/SimulationInterfaces/TagFilter.h Include/SimulationInterfaces/Bounds.h Include/SimulationInterfaces/NamedPoseManagerRequestBus.h + Include/SimulationInterfaces/Resource.h + Include/SimulationInterfaces/WorldResource.h Include/SimulationInterfaces/SimulationEntityManagerRequestBus.h Include/SimulationInterfaces/SimulationInterfacesTypeIds.h Include/SimulationInterfaces/SimulationMangerRequestBus.h Include/SimulationInterfaces/SimulationFeaturesAggregatorRequestBus.h + Include/SimulationInterfaces/LevelManagerRequestBus.h Include/SimulationInterfaces/ROS2SimulationInterfacesTypeIds.h Include/SimulationInterfaces/ROS2SimulationInterfacesRequestBus.h ) diff --git a/Gems/SimulationInterfaces/Code/simulationinterfaces_editor_private_files.cmake b/Gems/SimulationInterfaces/Code/simulationinterfaces_editor_private_files.cmake index 37e857558d..bb4fdeb8cd 100644 --- a/Gems/SimulationInterfaces/Code/simulationinterfaces_editor_private_files.cmake +++ b/Gems/SimulationInterfaces/Code/simulationinterfaces_editor_private_files.cmake @@ -13,6 +13,8 @@ set(FILES Source/Tools/SimulationEntitiesManagerEditor.h Source/Tools/SimulationFeaturesAggregatorEditor.cpp Source/Tools/SimulationFeaturesAggregatorEditor.h + Source/Tools/LevelManagerEditor.cpp + Source/Tools/LevelManagerEditor.h Source/Tools/ROS2SimulationInterfacesEditorSystemComponent.cpp Source/Tools/ROS2SimulationInterfacesEditorSystemComponent.h Source/Components/NamedPoseEditorComponent.cpp diff --git a/Gems/SimulationInterfaces/Code/simulationinterfaces_private_files.cmake b/Gems/SimulationInterfaces/Code/simulationinterfaces_private_files.cmake index d4c48d9287..88cf00c907 100644 --- a/Gems/SimulationInterfaces/Code/simulationinterfaces_private_files.cmake +++ b/Gems/SimulationInterfaces/Code/simulationinterfaces_private_files.cmake @@ -17,6 +17,8 @@ set(FILES Source/Clients/NamedPosesManager.h Source/Clients/SimulationFeaturesAggregator.cpp Source/Clients/SimulationFeaturesAggregator.h + Source/Clients/LevelManager.cpp + Source/Clients/LevelManager.h Source/Clients/ROS2SimulationInterfacesSystemComponent.cpp Source/Clients/ROS2SimulationInterfacesSystemComponent.h Source/Interfaces/IROS2HandlerBase.h @@ -52,6 +54,15 @@ set(FILES Source/Services/GetSimulationStateServiceHandler.h Source/Services/StepSimulationServiceHandler.cpp Source/Services/StepSimulationServiceHandler.h + Source/Services/GetAvailableWorldsServiceHandler.cpp + Source/Services/GetAvailableWorldsServiceHandler.h + Source/Services/GetCurrentWorldServiceHandler.cpp + Source/Services/GetCurrentWorldServiceHandler.h + Source/Services/LoadWorldServiceHandler.cpp + Source/Services/LoadWorldServiceHandler.h + Source/Services/UnloadWorldServiceHandler.cpp + Source/Services/UnloadWorldServiceHandler.h + Source/Utils/RegistryUtils.cpp Source/Utils/RegistryUtils.h Source/Utils/Utils.h diff --git a/Gems/SimulationInterfaces/gem.json b/Gems/SimulationInterfaces/gem.json index 62120684dd..15819ae87f 100644 --- a/Gems/SimulationInterfaces/gem.json +++ b/Gems/SimulationInterfaces/gem.json @@ -1,6 +1,6 @@ { "gem_name": "SimulationInterfaces", - "version": "1.1.0", + "version": "2.0.0", "display_name": "SimulationInterfaces", "license": "Apache-2.0 ", "license_url": "https://opensource.org/licenses/Apache-2.0",