You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/Framework/AzFramework/AzFramework/Asset/AssetSystemComponent.cpp

870 lines
38 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzFramework/Asset/AssetSystemComponent.h>
#include <AzCore/Asset/AssetManagerBus.h>
#include <AzCore/std/string/conversions.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/IStreamer.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/std/chrono/chrono.h>
#include <AzCore/std/string/conversions.h>
#include <AzCore/Debug/EventTrace.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzFramework/Asset/AssetCatalogBus.h>
#include <AzFramework/Asset/AssetProcessorMessages.h>
#include <AzFramework/Asset/AssetSeedList.h>
#include <AzFramework/Asset/NetworkAssetNotification_private.h>
#include <AzFramework/Asset/Benchmark/BenchmarkCommands.h>
#include <AzFramework/Network/AssetProcessorConnection.h>
namespace AzFramework
{
namespace AssetBenchmark
{
// These are specifically registered here instead of in BenchmarkCommands.cpp to avoid dead code stripping.
// If BenchmarkCommands.cpp only contained the functions and the calls to AZ_CONSOLEFREEFUNC, there would be
// no external references into that compilation unit, which signals the linker to remove the entire file.
AZ_CONSOLEFREEFUNC(BenchmarkLoadAssetList, AZ::ConsoleFunctorFlags::Null, "Time the loading of all the listed asset names. "
"This will load any assets previously added via 'BenchmarkAddAssetToList' as well as any directly listed with this command.");
AZ_CONSOLEFREEFUNC(BenchmarkClearAssetList, AZ::ConsoleFunctorFlags::Null,
"Clear the list of assets to load with 'BenchmarkLoadAssetList'");
AZ_CONSOLEFREEFUNC(BenchmarkAddAssetsToList, AZ::ConsoleFunctorFlags::Null,
"Add asset(s) to the list of assets to load with 'BenchmarkLoadAssetList'");
AZ_CONSOLEFREEFUNC(BenchmarkLoadAllAssets, AZ::ConsoleFunctorFlags::Null, "Time the loading of all assets in the catalog");
AZ_CONSOLEFREEFUNC(BenchmarkLoadAllAssetsSynchronous, AZ::ConsoleFunctorFlags::Null,
"Time the loading of all assets in the catalog synchronously");
}
namespace AssetSystem
{
void OnAssetSystemMessage(unsigned int /*typeId*/, const void* buffer, unsigned int bufferSize, AZ::SerializeContext* context)
{
AssetNotificationMessage message;
// note that we forbid asset loading and we set STRICT mode. These messages are all the kind of message that is supposed to be transmitted between the
// same version of software, and are created at runtime, not loaded from disk, so they should not contain errors - if they do, it requires investigation.
if (!AZ::Utils::LoadObjectFromBufferInPlace(buffer, bufferSize, message, context, AZ::ObjectStream::FilterDescriptor(&AZ::Data::AssetFilterNoAssetLoading, AZ::ObjectStream::FILTERFLAG_STRICT)))
{
AZ_WarningOnce("AssetSystem", false, "AssetNotificationMessage received but unable to deserialize it. Is AssetProcessor.exe up to date?");
return;
}
if (message.m_data.length() > AZ_MAX_PATH_LEN)
{
auto maxPath = message.m_data.substr(0, AZ_MAX_PATH_LEN - 1);
AZ_Warning("AssetSystem", false, "HotUpdate: filename too long(%zd) : %s...", bufferSize, maxPath.c_str());
return;
}
switch (message.m_type)
{
case AssetNotificationMessage::AssetChanged:
{
// Used only to communicate to AssetCatalogs - no other system should rely on this
// Instead listen to AssetCatalogEventBus::OnAssetChanged
// This is a DIRECT call so that the catalog can update itself immediately so that it maintains as accurate a view of the current state of assets as possible.
// Attempting to queue this on the main thread has led to issues previously where systems start receiving
// asset change/remove notifications before all of the network catalog updates have been applied and start querying the state of other assets which haven't been updated yet.
AzFramework::AssetSystem::NetworkAssetUpdateInterface* notificationInterface = AZ::Interface<AzFramework::AssetSystem::NetworkAssetUpdateInterface>::Get();
if (notificationInterface)
{
notificationInterface->AssetChanged(message);
}
}
break;
case AssetNotificationMessage::AssetRemoved:
{
// Used only to communicate to AssetCatalogs - no other system should rely on this
// Instead listen to AssetCatalogEventBus::OnAssetRemoved
// This is a DIRECT call so that the catalog can update itself immediately so that it maintains as accurate a view of the current state of assets as possible.
// Attempting to queue this on the main thread has led to issues previously where systems start receiving
// asset change/remove notifications before all of the network catalog updates have been applied and start querying the state of other assets which haven't been updated yet.
AzFramework::AssetSystem::NetworkAssetUpdateInterface* notificationInterface = AZ::Interface<AzFramework::AssetSystem::NetworkAssetUpdateInterface>::Get();
if (notificationInterface)
{
notificationInterface->AssetRemoved(message);
}
}
break;
case AssetNotificationMessage::JobFileClaimed:
{
auto streamer = AZ::Interface<AZ::IO::IStreamer>::Get();
if (streamer)
{
// The Asset Processor is about to write a product file that could have been loaded through AZ::IO::Streamer so
// flush it from any caches so stale data isn't used on reload and to make sure any file handles are released.
streamer->QueueRequest(streamer->FlushCache(message.m_data));
}
AssetSystemInfoBus::Broadcast(&AssetSystemInfoBus::Events::AssetFileClaimed, message.m_data);
}
break;
case AssetNotificationMessage::JobFileReleased:
{
AssetSystemInfoBus::Broadcast(&AssetSystemInfoBus::Events::AssetFileReleased, message.m_data);
}
break;
case AssetNotificationMessage::JobStarted:
{
AssetSystemInfoBus::Broadcast(&AssetSystemInfoBus::Events::AssetCompilationStarted, message.m_data);
}
break;
case AssetNotificationMessage::JobCompleted:
{
AssetSystemInfoBus::Broadcast(&AssetSystemInfoBus::Events::AssetCompilationSuccess, message.m_data);
}
break;
case AssetNotificationMessage::JobFailed:
{
AssetSystemInfoBus::Broadcast(&AssetSystemInfoBus::Events::AssetCompilationFailed, message.m_data);
}
break;
case AssetNotificationMessage::JobCount:
{
int numberOfAssets = atoi(message.m_data.c_str());
AssetSystemInfoBus::Broadcast(&AssetSystemInfoBus::Events::CountOfAssetsInQueue, numberOfAssets);
}
break;
default:
AZ_WarningOnce("AssetSystem", false, "Unknown AssetNotificationMessage type received from network. Is AssetProcessor.exe up to date?");
break;
}
}
void AssetSystemComponent::Init()
{
m_socketConn.reset(new AssetProcessorConnection());
}
void AssetSystemComponent::Activate()
{
AZ::SerializeContext* context = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationRequests::GetSerializeContext);
EnableSocketConnection();
m_cbHandle = m_socketConn->AddMessageHandler(AZ_CRC("AssetProcessorManager::AssetNotification", 0xd6191df5),
[context](unsigned int typeId, unsigned int /*serial*/, const void* data, unsigned int dataLength)
{
if (dataLength)
{
OnAssetSystemMessage(typeId, data, dataLength, context);
}
});
AssetSystemRequestBus::Handler::BusConnect();
AZ::SystemTickBus::Handler::BusConnect();
AssetSystemStatusBus::Broadcast(&AssetSystemStatusBus::Events::AssetSystemAvailable);
}
void AssetSystemComponent::Deactivate()
{
AssetSystemStatusBus::Broadcast(&AssetSystemStatusBus::Events::AssetSystemUnavailable);
AZ::SystemTickBus::Handler::BusDisconnect();
AssetSystemRequestBus::Handler::BusDisconnect();
m_socketConn->RemoveMessageHandler(AZ_CRC("AssetProcessorManager::AssetNotification", 0xd6191df5), m_cbHandle);
m_socketConn->Disconnect(true);
DisableSocketConnection();
}
void AssetSystemComponent::Reflect(AZ::ReflectContext* context)
{
NegotiationMessage::Reflect(context);
BaseAssetProcessorMessage::Reflect(context);
RequestAssetStatus::Reflect(context);
RequestEscalateAsset::Reflect(context);
ResponseAssetProcessorStatus::Reflect(context);
RequestAssetProcessorStatus::Reflect(context);
ResponseAssetStatus::Reflect(context);
RequestPing::Reflect(context);
ResponsePing::Reflect(context);
// Requests
GetUnresolvedDependencyCountsRequest::Reflect(context);
GetRelativeProductPathFromFullSourceOrProductPathRequest::Reflect(context);
GenerateRelativeSourcePathRequest::Reflect(context);
GetFullSourcePathFromRelativeProductPathRequest::Reflect(context);
SourceAssetInfoRequest::Reflect(context);
AssetInfoRequest::Reflect(context);
AssetDependencyInfoRequest::Reflect(context);
RegisterSourceAssetRequest::Reflect(context);
UnregisterSourceAssetRequest::Reflect(context);
ShowAssetProcessorRequest::Reflect(context);
ShowAssetInAssetProcessorRequest::Reflect(context);
FileOpenRequest::Reflect(context);
FileCloseRequest::Reflect(context);
FileReadRequest::Reflect(context);
FileWriteRequest::Reflect(context);
FileTellRequest::Reflect(context);
FileSeekRequest::Reflect(context);
FileIsReadOnlyRequest::Reflect(context);
PathIsDirectoryRequest::Reflect(context);
FileSizeRequest::Reflect(context);
FileModTimeRequest::Reflect(context);
FileExistsRequest::Reflect(context);
FileFlushRequest::Reflect(context);
PathCreateRequest::Reflect(context);
PathDestroyRequest::Reflect(context);
FileRemoveRequest::Reflect(context);
FileCopyRequest::Reflect(context);
FileRenameRequest::Reflect(context);
FindFilesRequest::Reflect(context);
FileTreeRequest::Reflect(context);
// Responses
GetUnresolvedDependencyCountsResponse::Reflect(context);
GetRelativeProductPathFromFullSourceOrProductPathResponse::Reflect(context);
GenerateRelativeSourcePathResponse::Reflect(context);
GetFullSourcePathFromRelativeProductPathResponse::Reflect(context);
SourceAssetInfoResponse::Reflect(context);
AssetInfoResponse::Reflect(context);
AssetDependencyInfoResponse::Reflect(context);
FileOpenResponse::Reflect(context);
FileReadResponse::Reflect(context);
FileWriteResponse::Reflect(context);
FileTellResponse::Reflect(context);
FileSeekResponse::Reflect(context);
FileIsReadOnlyResponse::Reflect(context);
PathIsDirectoryResponse::Reflect(context);
FileSizeResponse::Reflect(context);
FileModTimeResponse::Reflect(context);
FileExistsResponse::Reflect(context);
FileFlushResponse::Reflect(context);
PathCreateResponse::Reflect(context);
PathDestroyResponse::Reflect(context);
FileRemoveResponse::Reflect(context);
FileCopyResponse::Reflect(context);
FileRenameResponse::Reflect(context);
FindFilesResponse::Reflect(context);
FileTreeResponse::Reflect(context);
SaveAssetCatalogRequest::Reflect(context);
SaveAssetCatalogResponse::Reflect(context);
AssetNotificationMessage::Reflect(context);
AssetSeedListReflector::Reflect(context);
SeedInfo::Reflect(context);
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<AssetSystemComponent, AZ::Component>()
;
}
}
void AssetSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("AssetProcessorConnection", 0xf0cd75cd));
}
void AssetSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("AssetProcessorConnection", 0xf0cd75cd));
}
void AssetSystemComponent::EnableSocketConnection()
{
AZ_Assert(!SocketConnection::GetInstance(), "You can only have one AZ::SocketConnection");
if (!SocketConnection::GetInstance())
{
SocketConnection::SetInstance(m_socketConn.get());
}
}
void AssetSystemComponent::DisableSocketConnection()
{
SocketConnection* socketConnection = SocketConnection::GetInstance();
if (socketConnection && socketConnection == m_socketConn.get())
{
SocketConnection::SetInstance(nullptr);
}
}
//////////////////////////////////////////////////////////////////////////
// SystemTickBus overrides
void AssetSystemComponent::OnSystemTick()
{
AZ_TRACE_METHOD();
LegacyAssetEventBus::ExecuteQueuedEvents();
}
bool AssetSystemComponent::SetAssetProcessorIP(AZStd::string_view ip)
{
if (ConnectedWithAssetProcessor())
{
AZ_Warning("AssetSystem", false, "Cannot change ip while already connected");
return false;
}
m_assetProcessorIP = ip;
return true;
}
bool AssetSystemComponent::SetAssetProcessorPort(AZ::u16 port)
{
if (ConnectedWithAssetProcessor())
{
AZ_Warning("AssetSystem", false, "Cannot change port while already connected");
return false;
}
m_assetProcessorPort = port;
return true;
}
void AssetSystemComponent::SetAssetProcessorBranchToken(AZStd::string_view branchToken)
{
if (m_assetProcessorBranchToken != branchToken)
{
m_configured = false;
m_assetProcessorBranchToken = branchToken;
}
}
void AssetSystemComponent::SetAssetProcessorProjectName(AZStd::string_view projectName)
{
if (m_assetProcessorProjectName != projectName)
{
m_configured = false;
m_assetProcessorProjectName = projectName;
}
}
void AssetSystemComponent::SetAssetProcessorPlatform(AZStd::string_view platform)
{
if (m_assetProcessorPlatform != platform)
{
m_configured = false;
m_assetProcessorPlatform = platform;
}
}
void AssetSystemComponent::SetAssetProcessorIdentifier(AZStd::string_view identifier)
{
if (m_assetProcessorIdentifier != identifier)
{
m_configured = false;
m_assetProcessorIdentifier = identifier;
}
}
bool AssetSystemComponent::ConfigureSocketConnection()
{
AZ_Assert(m_socketConn.get(), "SocketConnection doesn't exist! Ensure AssetSystemComponent::Init was called");
if (ConnectedWithAssetProcessor()) // Don't allow changing the IP while connected
{
AZ_Warning("AssetSystem", false, "Cannot configure while connected");
return false;
}
auto apConnection = azrtti_cast<AssetProcessorConnection*>(m_socketConn.get());
apConnection->Configure(m_assetProcessorBranchToken.c_str(),
m_assetProcessorPlatform.c_str(),
m_assetProcessorIdentifier.c_str(),
m_assetProcessorProjectName.c_str());
m_configured = true;
return true;
}
bool AssetSystemComponent::StartConnectToAssetProcessor()
{
AZ_Assert(m_socketConn.get(), "SocketConnection doesn't exist! Ensure AssetSystemComponent::Init was called");
if (ConnectedWithAssetProcessor())
{
AZ_Warning("AssetSystem", false, "Cannot connect while already connected.");
return false;
}
if (!m_configured)
{
AZ_Warning("AssetSystem", false, "SocketConnection was not configured before calling StartConnectToAssetProcessor!!! Ensure AssetSystemComponent::ConfigureSocketConnection was called after changing any setting.");
return false;
}
//connect is async
AZ_TracePrintf("Asset System Connection", "Asset Processor Connection IP: %s, port: %hu, branch token %s\n", m_assetProcessorIP.c_str(), m_assetProcessorPort, m_assetProcessorBranchToken.c_str());
auto apConnection = azrtti_cast<AssetProcessorConnection*>(m_socketConn.get());
apConnection->Connect(m_assetProcessorIP.c_str(), m_assetProcessorPort);
return true;
}
bool AssetSystemComponent::StartConnectFromAssetProcessor()
{
AZ_Assert(m_socketConn.get(), "SocketConnection doesn't exist! Ensure AssetSystemComponent::Init was called");
if (ConnectedWithAssetProcessor()) // Don't allow changing the IP while connected
{
AZ_Warning("AssetSystem", false, "Cannot connect while connected.");
return false;
}
if (!m_configured)
{
AZ_Warning("AssetSystem", false, "SocketConnection was not configured before calling StartConnectFromAssetProcessor!!! Ensure AssetSystemComponent::ConfigureSocketConnection was called after changing any setting.");
return false;
}
//listen is async
//instances always listen on port 22229, currently its not configurable
auto apConnection = azrtti_cast<AssetProcessorConnection*>(m_socketConn.get());
apConnection->Listen(22229);
return true;
}
bool AssetSystemComponent::ConnectToAssetProcessor(const ConnectionSettings& connectionSettings)
{
if (connectionSettings.m_loggingCallback)
{
connectionSettings.m_loggingCallback("Connecting to Asset Processor...\n");
}
if (!SetAssetProcessorIP(connectionSettings.m_assetProcessorIp))
{
AZ_Warning("AssetSystem", false, "SetAssetProcessorIP() has failed!!!.");
return false;
}
if (!SetAssetProcessorPort(connectionSettings.m_assetProcessorPort))
{
AZ_Warning("AssetSystem", false, "SetAssetProcessorPort() has failed!!!.");
return false;
}
SetAssetProcessorBranchToken(connectionSettings.m_branchToken);
SetAssetProcessorProjectName(connectionSettings.m_projectName);
SetAssetProcessorPlatform(connectionSettings.m_assetPlatform);
SetAssetProcessorIdentifier(connectionSettings.m_connectionIdentifier);
if (!ConfigureSocketConnection())
{
AZ_Warning("AssetSystem", false, "ConfigureSocketConnection() has failed!!!.");
return false;
}
if (!StartConnectToAssetProcessor())
{
AZ_Warning("AssetSystem", false, "StartConnectToAssetProcessor() has failed!!!");
return false;
}
return WaitUntilAssetProcessorConnected(connectionSettings.m_connectTimeout);
}
bool AssetSystemComponent::ConnectFromAssetProcessor(const ConnectionSettings& connectionSettings)
{
if (connectionSettings.m_loggingCallback)
{
connectionSettings.m_loggingCallback("Listening for Asset Processor connection...\n");
}
SetAssetProcessorBranchToken(connectionSettings.m_branchToken);
SetAssetProcessorProjectName(connectionSettings.m_projectName);
SetAssetProcessorPlatform(connectionSettings.m_assetPlatform);
SetAssetProcessorIdentifier(connectionSettings.m_connectionIdentifier);
if(!ConfigureSocketConnection())
{
AZ_Warning("AssetSystem", false, "ConfigureSocketConnection() has failed!!!");
return false;
}
if (!StartConnectFromAssetProcessor())
{
AZ_Warning("AssetSystem", false, "StartConnectFromAssetProcessor() has failed!!!");
return false;
}
return WaitUntilAssetProcessorConnected(connectionSettings.m_connectTimeout);
}
//////////////////////////////////////////////////////////////////////////
// AssetSystemRequestBus::Handler overrides
bool AssetSystemComponent::EstablishAssetProcessorConnection(const ConnectionSettings& connectionSettings)
{
bool connectionEstablished{};
if (connectionSettings.m_connectionDirection == ConnectionSettings::ConnectionDirection::ConnectToAssetProcessor)
{
connectionEstablished = ConnectToAssetProcessor(connectionSettings);
}
else
{
connectionEstablished = ConnectFromAssetProcessor(connectionSettings);
}
if (!connectionEstablished)
{
bool failedNegotiation = NegotiationWithAssetProcessorFailed();
if (failedNegotiation)
{
AZ_Error(connectionSettings.m_connectionIdentifier.c_str(), false, "Negotiation with asset processor failed");
return false;
}
#if AZ_TRAIT_OS_IS_HOST_OS_PLATFORM
if (connectionSettings.m_launchAssetProcessorOnFailedConnection)
{
if (!LaunchAssetProcessor())
{
if (connectionSettings.m_waitForConnect)
{
AZ_Error(connectionSettings.m_connectionIdentifier.c_str(), false, "Launch asset processor failed");
return false;
}
else
{
AZ_Warning(connectionSettings.m_connectionIdentifier.c_str(), false, "Launch asset processor failed");
}
}
else
{
AZStd::chrono::time_point startConnectFromLaunchTime = AZStd::chrono::system_clock::now();
connectionEstablished = WaitUntilAssetProcessorConnected(connectionSettings.m_launchTimeout);
AZStd::chrono::time_point endConnectFromLaunchTime = AZStd::chrono::system_clock::now();
if (!connectionEstablished && NegotiationWithAssetProcessorFailed())
{
AZ_Error(connectionSettings.m_connectionIdentifier.c_str(), false, "Negotiation with asset processor failed");
}
else
{
AZStd::chrono::duration<float> launchToConnectTime{ endConnectFromLaunchTime - startConnectFromLaunchTime };
if (connectionSettings.m_loggingCallback)
{
connectionSettings.m_loggingCallback(AZStd::fixed_string<128>::format("Launched Asset Processor and received connection in %f seconds\n",
launchToConnectTime.count()));
}
}
}
}
#endif
while (!connectionEstablished && connectionSettings.m_waitForConnect)
{
constexpr AZStd::chrono::seconds aSecond(1);
connectionEstablished = WaitUntilAssetProcessorConnected(aSecond);
if (!connectionEstablished && NegotiationWithAssetProcessorFailed())
{
AZ_Error(connectionSettings.m_connectionIdentifier.c_str(), false, "Negotiation with asset processor failed");
break;
}
}
}
// If the wait until asset processor is ready option is unset
// The return only whether a successful connection to the Asset Processor has taken place
if (connectionEstablished && connectionSettings.m_waitUntilAssetProcessorIsReady)
{
// regardless of what is set in the bootstrap wait for AP to be ready
// wait a maximum of 100 milliseconds and pump the system event loop until empty
struct AssetsInQueueNotification
: public AzFramework::AssetSystemInfoBus::Handler
{
AssetsInQueueNotification(const ConnectionSettings::LoggingCallback& callback)
: m_loggingCallback{ callback }
{
}
void CountOfAssetsInQueue(const int& count) override
{
// Pad to 7 digits as there should be any reasonable amount of jobs that are in the millions
// Carriage Return is used here to overwrite the current line of output
if (m_loggingCallback)
{
m_loggingCallback(AZStd::fixed_string<128>::format("Asset Processor working... %7d jobs remaining\r", count));
}
}
const ConnectionSettings::LoggingCallback& m_loggingCallback;
};
AssetsInQueueNotification assetsInQueueNotifcation(connectionSettings.m_loggingCallback);
assetsInQueueNotifcation.BusConnect();
if (connectionSettings.m_loggingCallback)
{
connectionSettings.m_loggingCallback("Asset Processor working...\r");
}
bool assetProcessorIsReady = AssetProcessorIsReady() || WaitUntilAssetProcessorReady(connectionSettings.m_waitForReadyTimeout);
assetsInQueueNotifcation.BusDisconnect();
return connectionEstablished && assetProcessorIsReady;
}
return connectionEstablished;
}
bool AssetSystemComponent::WaitUntilAssetProcessorConnected(AZStd::chrono::duration<float> timeout)
{
AZStd::chrono::system_clock::time_point start = AZStd::chrono::system_clock::now();
while (!ConnectedWithAssetProcessor() && AZStd::chrono::duration_cast<AZStd::chrono::milliseconds>(AZStd::chrono::system_clock::now() - start) < timeout)
{
if (NegotiationWithAssetProcessorFailed())
{
EBUS_EVENT(AzFramework::AssetSystemConnectionNotificationsBus, NegotiationFailed);
StartDisconnectingAssetProcessor();
return false;
}
//yield
AZStd::this_thread::yield();
}
return ConnectedWithAssetProcessor();
}
bool AssetSystemComponent::WaitUntilAssetProcessorReady(AZStd::chrono::duration<float> timeout)
{
if (!ConnectedWithAssetProcessor()) //don't wait if not connected
{
return false;
}
// while we wait, let's get some ping times.
float pingTime = GetAssetProcessorPingTimeMilliseconds();
if (pingTime > 0.0f)
{
AZ_TracePrintf("AssetSystem", "Ping time to asset processor: %0.2f milliseconds\n", pingTime);
}
AZStd::chrono::system_clock::time_point start = AZStd::chrono::system_clock::now();
bool isAssetProcessorReady = false;
while (!isAssetProcessorReady && (AZStd::chrono::system_clock::now() - start) < timeout)
{
AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::PumpSystemEventLoopUntilEmpty);
if (!ConnectedWithAssetProcessor())
{
//If we are here than it means we were connected but have lost connection with AP
AZ_Warning("AssetSystem", false, "Lost the connection to the Asset Processor!\nMake sure the Asset Processor is running.");
return false;
}
//Keep asking the AP about its status, until it is ready
isAssetProcessorReady = AssetProcessorIsReady();
if(!isAssetProcessorReady)
{
// Throttle this, each loop actually sends network traffic to the AP and there's no point in running at 100x a second, but 10x is smooth.
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(100)); // on some systems, PumpSystemEventLoopUntilEmpty may not sleep.
}
}
return isAssetProcessorReady;
}
bool AssetSystemComponent::ConnectedWithAssetProcessor()
{
AZ_Assert(m_socketConn.get(), "SocketConnection doesn't exist! Ensure AssetSystemComponent::Init was called");
auto apConnection = azrtti_cast<AssetProcessorConnection*>(m_socketConn.get());
return apConnection->IsConnected();
}
bool AssetSystemComponent::NegotiationWithAssetProcessorFailed()
{
AZ_Assert(m_socketConn.get(), "SocketConnection doesn't exist! Ensure AssetSystemComponent::Init was called");
auto apConnection = azrtti_cast<AssetProcessorConnection*>(m_socketConn.get());
return apConnection->NegotiationFailed();
}
bool AssetSystemComponent::AssetProcessorIsReady()
{
if (!ConnectedWithAssetProcessor()) //cant be ready if not connected
{
return false;
}
RequestAssetProcessorStatus request;
request.m_platform = m_assetProcessorPlatform;
ResponseAssetProcessorStatus response;
if (!SendRequest(request, response))
{
AZ_Warning("AssetSystem", false, "Failed to send Asset Processor Status request for platform %s.", m_assetProcessorPlatform.c_str());
return false;
}
return response.m_isAssetProcessorReady;
}
void AssetSystemComponent::StartDisconnectingAssetProcessor()
{
AZ_Assert(m_socketConn.get(), "SocketConnection doesn't exist! Ensure AssetSystemComponent::Init was called");
auto apConnection = azrtti_cast<AssetProcessorConnection*>(m_socketConn.get());
apConnection->Disconnect();
}
bool AssetSystemComponent::DisconnectedWithAssetProcessor()
{
AZ_Assert(m_socketConn.get(), "SocketConnection doesn't exist! Ensure AssetSystemComponent::Init was called");
auto apConnection = azrtti_cast<AssetProcessorConnection*>(m_socketConn.get());
return apConnection->IsDisconnected();
}
bool AssetSystemComponent::WaitUntilAssetProcessorDisconnected(AZStd::chrono::duration<float> timeout)
{
AZStd::chrono::system_clock::time_point start = AZStd::chrono::system_clock::now();
while (!DisconnectedWithAssetProcessor() && AZStd::chrono::duration_cast<AZStd::chrono::milliseconds>(AZStd::chrono::system_clock::now() - start) < timeout)
{
//yield
AZStd::this_thread::yield();
}
return DisconnectedWithAssetProcessor();
}
AssetStatus AssetSystemComponent::CompileAssetSync(const AZStd::string& assetPath)
{
return SendAssetStatusRequest(RequestAssetStatus(assetPath.c_str(), false, false));
}
AssetStatus AssetSystemComponent::CompileAssetSync_FlushIO(const AZStd::string& assetPath)
{
return SendAssetStatusRequest(RequestAssetStatus(assetPath.c_str(), false, true));
}
AssetStatus AssetSystemComponent::CompileAssetSyncById(const AZ::Data::AssetId& assetId)
{
return SendAssetStatusRequest(RequestAssetStatus(assetId, false, false));
}
AssetStatus AssetSystemComponent::CompileAssetSyncById_FlushIO(const AZ::Data::AssetId& assetId)
{
return SendAssetStatusRequest(RequestAssetStatus(assetId, false, true));
}
AssetStatus AssetSystemComponent::GetAssetStatus(const AZStd::string& assetPath)
{
return SendAssetStatusRequest(RequestAssetStatus(assetPath.c_str(), true, false));
}
AssetStatus AssetSystemComponent::GetAssetStatus_FlushIO(const AZStd::string& assetPath)
{
return SendAssetStatusRequest(RequestAssetStatus(assetPath.c_str(), true, true));
}
AssetStatus AssetSystemComponent::GetAssetStatusSearchType(const AZStd::string& assetPath, int searchType)
{
return SendAssetStatusRequest(RequestAssetStatus(assetPath.c_str(), true, false, searchType));
}
AssetStatus AssetSystemComponent::GetAssetStatusSearchType_FlushIO(const AZStd::string& assetPath, int searchType)
{
return SendAssetStatusRequest(RequestAssetStatus(assetPath.c_str(), true, true, searchType));
}
AssetStatus AssetSystemComponent::GetAssetStatusById(const AZ::Data::AssetId& assetId)
{
return SendAssetStatusRequest(RequestAssetStatus(assetId, true, false));
}
AssetStatus AssetSystemComponent::GetAssetStatusById_FlushIO(const AZ::Data::AssetId& assetId)
{
return SendAssetStatusRequest(RequestAssetStatus(assetId, true, true));
}
bool AssetSystemComponent::EscalateAssetByUuid(const AZ::Uuid& assetUuid)
{
if (ConnectedWithAssetProcessor())
{
RequestEscalateAsset request(assetUuid);
SendRequest(request);
return true;
}
return false; // not sent.
}
bool AssetSystemComponent::EscalateAssetBySearchTerm(AZStd::string_view searchTerm)
{
if (ConnectedWithAssetProcessor())
{
RequestEscalateAsset request(searchTerm.data());
SendRequest(request);
return true;
}
return false; // not sent.
}
void AssetSystemComponent::GetUnresolvedProductReferences(AZ::Data::AssetId assetId, AZ::u32& unresolvedAssetIdReferences, AZ::u32& unresolvedPathReferences)
{
AZ_Assert(m_socketConn.get(), "SocketConnection doesn't exist! Ensure AssetSystemComponent::Init was called");
unresolvedPathReferences = unresolvedAssetIdReferences = 0;
if (ConnectedWithAssetProcessor())
{
GetUnresolvedDependencyCountsRequest request(assetId);
GetUnresolvedDependencyCountsResponse response;
if (SendRequest(request, response))
{
unresolvedAssetIdReferences = response.m_unresolvedAssetIdReferences;
unresolvedPathReferences = response.m_unresolvedPathReferences;
}
}
}
AssetStatus AssetSystemComponent::SendAssetStatusRequest(const RequestAssetStatus& request)
{
AssetStatus eStatus = AssetStatus_Unknown;
if (ConnectedWithAssetProcessor())
{
ResponseAssetStatus response;
SendRequest(request, response);
eStatus = static_cast<AssetStatus>(response.m_assetStatus);
}
return eStatus;
}
float AssetSystemComponent::GetAssetProcessorPingTimeMilliseconds()
{
if (!ConnectedWithAssetProcessor())
{
return 0.0f;
}
AZStd::chrono::system_clock::time_point beforePing = AZStd::chrono::system_clock::now();
RequestPing pingeRequest;
ResponsePing pingRespose;
if (SendRequest(pingeRequest, pingRespose))
{
AZStd::chrono::duration<float, AZStd::milli> difference = AZStd::chrono::duration_cast<AZStd::chrono::duration<float, AZStd::milli> >(AZStd::chrono::system_clock::now() - beforePing);
return difference.count();
}
return 0.0f;
}
bool AssetSystemComponent::SaveCatalog()
{
if (!ConnectedWithAssetProcessor()) //cant be ready if not connected
{
return false;
}
SaveAssetCatalogRequest saveCatalogRequest;
SaveAssetCatalogResponse saveCatalogRespose;
if (SendRequest(saveCatalogRequest, saveCatalogRespose))
{
return saveCatalogRespose.m_saved;
}
return false;
}
} // namespace AssetSystem
} // namespace AzFramework