/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace PythonAssetBuilder { void PythonAssetBuilderSystemComponent::Reflect(AZ::ReflectContext* context) { PythonBuilderNotificationHandler::Reflect(context); PythonBuilderWorker::Reflect(context); if (AZ::SerializeContext* serialize = azrtti_cast(context)) { const AZStd::vector systemTags({ AssetBuilderSDK::ComponentTags::AssetBuilder }); serialize->Class() ->Version(0) ->Attribute(AZ::Edit::Attributes::SystemComponentTags, systemTags) ; } if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) { behaviorContext->EBus("PythonAssetBuilderRequestBus") ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) ->Attribute(AZ::Script::Attributes::Module, "asset.builder") ->Event("RegisterAssetBuilder", &PythonAssetBuilderRequestBus::Events::RegisterAssetBuilder) ->Event("GetExecutableFolder", &PythonAssetBuilderRequestBus::Events::GetExecutableFolder) ; behaviorContext->EBus("PythonBuilderRequestBus") ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) ->Attribute(AZ::Script::Attributes::Module, "asset.entity") ->Event("WriteSliceFile", &PythonBuilderRequestBus::Events::WriteSliceFile) ->Event("CreateEditorEntity", &PythonBuilderRequestBus::Events::CreateEditorEntity) ; } } void PythonAssetBuilderSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) { provided.push_back(AZ_CRC_CE("PythonAssetBuilderService")); } void PythonAssetBuilderSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) { incompatible.push_back(AZ_CRC_CE("PythonAssetBuilderService")); } void PythonAssetBuilderSystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent) { dependent.push_back(EditorPythonBindings::PythonMarshalingService); dependent.push_back(EditorPythonBindings::PythonReflectionService); dependent.push_back(EditorPythonBindings::PythonEmbeddedService); } void PythonAssetBuilderSystemComponent::Init() { m_messageSink = AZStd::make_shared(); } void PythonAssetBuilderSystemComponent::Activate() { PythonAssetBuilderRequestBus::Handler::BusConnect(); if (auto&& pythonInterface = AZ::Interface::Get()) { pythonInterface->StartPython(true); } PythonBuilderRequestBus::Handler::BusConnect(); } void PythonAssetBuilderSystemComponent::Deactivate() { PythonBuilderRequestBus::Handler::BusDisconnect(); m_messageSink.reset(); if (PythonAssetBuilderRequestBus::HasHandlers()) { PythonAssetBuilderRequestBus::Handler::BusDisconnect(); if (auto&& pythonInterface = AZ::Interface::Get()) { pythonInterface->StopPython(true); } } } AZ::Outcome PythonAssetBuilderSystemComponent::RegisterAssetBuilder(const AssetBuilderSDK::AssetBuilderDesc& desc) { const AZ::Uuid busId = desc.m_busId; if (m_pythonBuilderWorkerMap.find(busId) != m_pythonBuilderWorkerMap.end()) { AZStd::string busIdString(busId.ToString()); AZStd::string failMessage = AZStd::string::format("Asset Builder of JobId:%s has already been created.", busIdString.c_str()); AZ_Warning(PythonBuilder, false, failMessage.c_str()); return AZ::Failure(failMessage); } // create a PythonBuilderWorker instance auto worker = AZStd::make_shared(); if (worker->ConfigureBuilderInformation(desc) == false) { return AZ::Failure(AZStd::string::format("Failed to configure builderId:%s", busId.ToString().c_str())); } m_pythonBuilderWorkerMap[busId] = worker; return AZ::Success(true); } AZ::Outcome PythonAssetBuilderSystemComponent::GetExecutableFolder() const { const char* exeFolderName = nullptr; AZ::ComponentApplicationBus::BroadcastResult(exeFolderName, &AZ::ComponentApplicationRequests::GetExecutableFolder); if (exeFolderName) { return AZ::Success(AZStd::string(exeFolderName)); } return AZ::Failure(AZStd::string("GetExecutableFolder access is missing.")); } AZ::Outcome PythonAssetBuilderSystemComponent::CreateEditorEntity(const AZStd::string& name) { AZ::EntityId entityId; AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult( entityId, &AzToolsFramework::EditorEntityContextRequestBus::Events::CreateNewEditorEntity, name.c_str()); if (entityId.IsValid() == false) { return AZ::Failure("Failed to CreateNewEditorEntity."); } AZ::Entity* entity = nullptr; AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationRequests::FindEntity, entityId); if (entity == nullptr) { return AZ::Failure(AZStd::string::format("Failed to find created entityId %s", entityId.ToString().c_str())); } entity->Deactivate(); AzToolsFramework::EditorEntityContextRequestBus::Broadcast( &AzToolsFramework::EditorEntityContextRequestBus::Events::AddRequiredComponents, *entity); entity->Activate(); return AZ::Success(entityId); } AZ::Outcome PythonAssetBuilderSystemComponent::WriteSliceFile( AZStd::string_view filename, AZStd::vector entityList, bool makeDynamic) { using namespace AzToolsFramework::SliceUtilities; AZ::SerializeContext* serializeContext = nullptr; AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext); if (serializeContext == nullptr) { return AZ::Failure("GetSerializeContext failed"); } // transaction->Commit() requires the "@user@" alias if (AZ::IO::FileIOBase::GetInstance()->GetAlias("@user@") == nullptr) { AZStd::string assetRoot; AzFramework::ApplicationRequests::Bus::BroadcastResult( assetRoot, &AzFramework::ApplicationRequests::Bus::Events::GetAssetRoot); AZStd::string userPath; AZ::StringFunc::Path::Join(assetRoot.c_str(), "AssetProcessorTemp", userPath); AZ::IO::FileIOBase::GetInstance()->SetAlias("@user@", userPath.c_str()); } // transaction->Commit() expects the file to exist and write-able AZ::IO::HandleType fileHandle; AZ::IO::LocalFileIO::GetInstance()->Open(filename.data(), AZ::IO::OpenMode::ModeWrite, fileHandle); if (fileHandle == AZ::IO::InvalidHandle) { return AZ::Failure( AZStd::string::format("Failed to create slice file %.*s", aznumeric_cast(filename.size()), filename.data())); } AZ::IO::LocalFileIO::GetInstance()->Close(fileHandle); AZ::u32 creationFlags = 0; if (makeDynamic) { creationFlags |= SliceTransaction::CreateAsDynamic; } SliceTransaction::TransactionPtr transaction = SliceTransaction::BeginNewSlice(nullptr, serializeContext, creationFlags); // add entities for (const AZ::EntityId& entityId : entityList) { auto addResult = transaction->AddEntity(entityId, SliceTransaction::SliceAddEntityFlags::DiscardSliceAncestry); if (!addResult) { return AZ::Failure(AZStd::string::format("Failed slice add entity: %s", addResult.GetError().c_str())); } } // commit to a file AZ::Data::AssetType sliceAssetType; auto resultCommit = transaction->Commit(filename.data(), nullptr, [&sliceAssetType]( SliceTransaction::TransactionPtr transactionPtr, const char* fullPath, const SliceTransaction::SliceAssetPtr& sliceAssetPtr) { sliceAssetType = sliceAssetPtr->GetType(); return AZ::Success(); }); if (!resultCommit) { return AZ::Failure(AZStd::string::format("Failed commit slice: %s", resultCommit.GetError().c_str())); } return AZ::Success(sliceAssetType); } }