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/Tools/AssetProcessor/native/FileServer/fileServer.cpp

1063 lines
32 KiB
C++

/*
* 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 <AzCore/PlatformIncl.h>
#include "fileServer.h"
#include <native/connection/connection.h>
#if !defined(APPLE) && !defined(LINUX)
#include <io.h>
#endif
#include "native/utilities/assetUtils.h"
#include <AzCore/IO/Path/Path.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzFramework/IO/LocalFileIO.h>
using namespace AZ::IO;
using namespace AzFramework::AssetSystem;
//#define VERBOSE_FILE_OPS
//////////////////////////////////////////////////////////////////////////////////////////
FileServer::FileServer(QObject* parent)
: QObject(parent)
{
m_realtimeMetrics = true;
setRealTimeMetrics(false);
//metrics
m_numOpenRequests = 0;
m_numCloseRequests = 0;
m_numOpened = 0;
m_numClosed = 0;
m_numReadRequests = 0;
m_numWriteRequests = 0;
m_numTellRequests = 0;
m_numSeekRequests = 0;
m_numIsReadOnlyRequests = 0;
m_numIsDirectoryRequests = 0;
m_numSizeRequests = 0;
m_numModificationTimeRequests = 0;
m_numExistsRequests = 0;
m_numFlushRequests = 0;
m_numCreatePathRequests = 0;
m_numDestroyPathRequests = 0;
m_numRemoveRequests = 0;
m_numCopyRequests = 0;
m_numRenameRequests = 0;
m_numFindFileNamesRequests = 0;
m_bytesRead = 0;
m_bytesWritten = 0;
m_bytesSent = 0;
m_bytesReceived = 0;
m_numOpenFiles = 0;
}
FileServer::~FileServer()
{
#ifdef REMOTEFILEIO_USE_PROFILING
g_profiler.DumpTimerDataToOutput();
g_profiler.DumpTimerDataToFile("../remotefileio_server_profile.txt");
#endif
}
void FileServer::SetSystemRoot(const QDir& systemRoot)
{
m_systemRoot = systemRoot;
m_displayRoot = m_systemRoot.absolutePath();
Q_EMIT RootFolderChanged();
}
void FileServer::setRealTimeMetrics(bool enable)
{
if (enable)
{
m_realtimeMetrics = true;
}
else if (m_realtimeMetrics)
{
m_realtimeMetrics = false;
UpdateMetrics();
}
}
void FileServer::ConnectionAdded(unsigned int connId, Connection* connection)
{
Q_UNUSED(connection);
// Connection has not completed negotiation yet, register to be notified
// when we know what platform is connected and map the @assets@ alias then
connect(connection, &Connection::AssetPlatformChanged, this, [this, connection]()
{
auto fileIO = m_fileIOs[connection->ConnectionId()];
if ((fileIO) && (!connection->AssetPlatforms().isEmpty())) // when someone disconnects, the asset platform may be cleared before disconnect is set.
{
QDir projectCacheRoot;
// Because the platform based aliases below can only be one platform at at a time we need to prefer a single platform in case multiple listening platforms
// exist on the same connection
QString assetPlatform = connection->AssetPlatforms().first();
if (!AssetUtilities::ComputeProjectCacheRoot(projectCacheRoot))
{
projectCacheRoot = m_systemRoot;
}
else
{
projectCacheRoot = QDir(projectCacheRoot.absoluteFilePath(assetPlatform));
}
const char* projectCachePath = projectCacheRoot.absolutePath().toUtf8().data();
fileIO->SetAlias("@assets@", projectCachePath);
fileIO->SetAlias("@root@", projectCachePath);
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
{
AZ::IO::Path projectUserPath;
settingsRegistry->Get(projectUserPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectUserPath);
fileIO->SetAlias("@user@", projectUserPath.c_str());
AZ::IO::Path logUserPath = projectUserPath / "log";
fileIO->SetAlias("@log@", logUserPath.c_str());
}
// note that the cache folder is auto-created only upon first use of VFS.
}
});
std::shared_ptr<AZ::IO::FileIOBase> fileIO = std::make_shared<AZ::IO::LocalFileIO>();
m_fileIOs[connId] = fileIO;
}
void FileServer::EnsureCacheFolderExists(int connId)
{
std::shared_ptr<AZ::IO::FileIOBase> fileIO = m_fileIOs[connId];
if (!fileIO)
{
return;
}
if (fileIO->GetAlias("@usercache@"))
{
// already created.
return;
}
AZ::IO::FixedMaxPath cacheUserPath;
auto settingsRegistry = AZ::SettingsRegistry::Get();
if (settingsRegistry->Get(cacheUserPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectUserPath))
{
cacheUserPath /= "Cache";
}
auto cacheDir = QString::fromUtf8(cacheUserPath.c_str(), aznumeric_cast<int>(cacheUserPath.Native().size()));
cacheDir = QDir::toNativeSeparators(cacheDir);
// the Cache-dir is special in that we don't allow sharing of cache dirs for multiple running
// apps of the same platform at the same time.
// we do this through the use of lock-files. Do not use QLockFile as QLockFile can't be shared
// with instances of lockfiles created through other means (such as the game itself running without VFS)
#if defined(AZ_PLATFORM_WINDOWS)
// todo: Future platforms such as MAC will need to use flock or NS to establish locks on folders
// note that if we DO support file locking we must ALWAYS create and lock the lock file
int attemptNumber = 0;
const int maxAttempts = 16;
QString originalPath = cacheDir;
while (attemptNumber < maxAttempts)
{
cacheDir = originalPath;
if (attemptNumber != 0)
{
cacheDir = QString("%1%2").arg(originalPath).arg(attemptNumber);
}
else
{
cacheDir = originalPath;
}
++attemptNumber; // do this here so we don't forget
QDir checkDir(cacheDir);
checkDir.mkpath(".");
// if the directory already exists, check for locked file
QString finalPath = QDir(cacheDir).absoluteFilePath("lockfile.txt");
// lock the file!
// cannot use QLockFile, it depends on everyone else which might lock the file also using QLockFile
// and actually cares about the files contents (your pid)
// note, the zero here after GENERIC_READ|GENERIC_WRITE indicates no share access at all!
std::wstring winFriendly = finalPath.toStdWString();
HANDLE lockHandle = CreateFileW(winFriendly.data(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, 0);
if (lockHandle != INVALID_HANDLE_VALUE)
{
m_locks[connId] = lockHandle;
break;
}
}
if (attemptNumber >= maxAttempts)
{
// do the best we can.
AZ_Warning("File Server", false, "Unable to establish a cache folder after %i attempts, using %s", attemptNumber, cacheDir.toUtf8().data());
cacheDir = originalPath;
}
#endif
fileIO->SetAlias("@usercache@", cacheDir.toUtf8().data());
}
void FileServer::ConnectionRemoved(unsigned int connId)
{
#if defined(AZ_PLATFORM_WINDOWS)
auto it = m_locks.find(connId);
if (it != m_locks.end())
{
if (it.value() != INVALID_HANDLE_VALUE)
{
CloseHandle(it.value());
}
m_locks.erase(it);
}
#endif
m_fileIOs.remove(connId);
}
void FileServer::UpdateMetrics()
{
if (!m_realtimeMetrics)
{
//update server metrics
Q_EMIT NumOpenRequestsChanged();
Q_EMIT NumCloseRequestsChanged();
Q_EMIT NumOpenedChanged();
Q_EMIT NumClosedChanged();
Q_EMIT NumReadRequestsChanged();
Q_EMIT NumWriteRequestsChanged();
Q_EMIT NumSeekRequestsChanged();
Q_EMIT NumTellRequestsChanged();
Q_EMIT NumIsReadOnlyRequestsChanged();
Q_EMIT NumIsDirectoryRequestsChanged();
Q_EMIT NumSizeRequestsChanged();
Q_EMIT NumModificationTimeRequestsChanged();
Q_EMIT NumExistsRequestsChanged();
Q_EMIT NumFlushRequestsChanged();
Q_EMIT NumCreatePathRequestsChanged();
Q_EMIT NumDestroyPathRequestsChanged();
Q_EMIT NumRemoveRequestsChanged();
Q_EMIT NumCopyRequestsChanged();
Q_EMIT NumRenameRequestsChanged();
Q_EMIT NumFindFileNamesRequestsChanged();
Q_EMIT BytesReadChanged();
Q_EMIT BytesWrittenChanged();
Q_EMIT BytesSentChanged();
Q_EMIT BytesReceivedChanged();
Q_EMIT NumOpenFilesChanged();
//update connections metrics
Q_EMIT UpdateConnectionMetrics();
//schedule another update one second from now
QTimer::singleShot(1000, this, SLOT(UpdateMetrics()));
}
}
template <class R>
inline void FileServer::Send(unsigned int connId, unsigned int serial, const R& response)
{
size_t bytesSent;
EBUS_EVENT_ID_RESULT(bytesSent, connId, AssetProcessor::ConnectionBus, SendResponse, serial, response);
m_bytesSent += bytesSent;
AddBytesSent(connId, bytesSent, m_realtimeMetrics);
}
template <class R>
inline bool FileServer::Recv(unsigned int connId, QByteArray payload, R& request)
{
bool readFromStream = AZ::Utils::LoadObjectFromBufferInPlace(payload.data(), payload.size(), request);
AZ_Assert(readFromStream, "FileServer::Recv: Could not deserialize from stream");
if (readFromStream)
{
m_bytesReceived += payload.size();
AddBytesReceived(connId, payload.size(), m_realtimeMetrics);
return true;
}
return false;
}
void FileServer::ProcessOpenRequest(unsigned int connId, unsigned int, unsigned int serial, QByteArray payload)
{
EnsureCacheFolderExists(connId);
m_numOpenRequests++;
//get the request
FileOpenRequest request;
if (!Recv(connId, payload, request))
{
AZ_Warning("FileServer", false, "ProcessOpenRequest: unable to read request");
// send a failure response
FileOpenResponse response(AZ::IO::InvalidHandle, static_cast<uint32_t>(ResultCode::Error));
Send(connId, serial, response);
}
const char* filePath = request.m_filePath.c_str();
AZ::IO::OpenMode mode = static_cast<AZ::IO::OpenMode>(request.m_mode);
AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
auto fileIO = m_fileIOs[connId];
RecordFileOp(fileIO.get(), "OPEN", filePath, (((mode& AZ::IO::OpenMode::ModeWrite) != AZ::IO::OpenMode::Invalid) ? "for write" : "for read"));
AZ::IO::Result res = fileIO->Open(filePath, mode, fileHandle);
if (res)
{
m_numOpenFiles++;
m_numOpened++;
}
uint32_t resultCode = static_cast<uint32_t>(res.GetResultCode());
FileOpenResponse response(fileHandle, resultCode);
Send(connId, serial, response);
AddOpenRequest(connId, m_realtimeMetrics);
if (res)
{
AddOpened(connId, m_realtimeMetrics);
}
if (m_realtimeMetrics)
{
Q_EMIT BytesSentChanged();
Q_EMIT NumOpenRequestsChanged();
Q_EMIT BytesReceivedChanged();
Q_EMIT NumOpenFilesChanged();
Q_EMIT NumOpenedChanged();
}
}
void FileServer::ProcessCloseRequest(unsigned int connId, unsigned int, unsigned int, QByteArray payload)
{
m_numCloseRequests++;
//get the request
FileCloseRequest request;
if (!Recv(connId, payload, request))
{
AZ_Error("FileServer", false, "Failed to deserialize FileCloseRequest for connection %u", connId);
return;
}
AZ::IO::HandleType fileHandle = request.m_fileHandle;
auto fileIO = m_fileIOs[connId];
RecordFileOp(fileIO.get(), "CLOSE", fileHandle, nullptr);
AZ::IO::Result res = fileIO->Close(fileHandle);
if (res)
{
m_numOpenFiles--;
m_numClosed++;
AddClosed(connId, m_realtimeMetrics);
}
AddCloseRequest(connId, m_realtimeMetrics);
if (m_realtimeMetrics)
{
Q_EMIT NumCloseRequestsChanged();
Q_EMIT BytesReceivedChanged();
Q_EMIT NumOpenFilesChanged();
Q_EMIT NumClosedChanged();
}
}
void FileServer::ProcessReadRequest(unsigned int connId, unsigned int, unsigned int serial, QByteArray payload)
{
m_numReadRequests++;
//get the request
FileReadRequest request;
if (!Recv(connId, payload, request))
{
FileReadResponse response(static_cast<uint32_t>(ResultCode::Error), nullptr, 0);
Send(connId, serial, response);
return;
}
AZ::IO::HandleType fileHandle = request.m_fileHandle;
uint64_t size = request.m_bytesToRead;
bool failOnFewerRead = request.m_failOnFewerRead;
FileReadResponse response;
response.m_data.resize_no_construct(request.m_bytesToRead);
AZ::u64 bytesRead = 0;
auto fileIO = m_fileIOs[connId];
AZStd::string moreInfo = AZStd::string::format("%llu bytes", static_cast<AZ::u64>(size));
RecordFileOp(fileIO.get(), "READ", fileHandle, moreInfo.c_str());
AZ::IO::Result res = fileIO->Read(fileHandle, response.m_data.data(), response.m_data.size(), failOnFewerRead, &bytesRead);
response.m_resultCode = static_cast<uint32_t>(res.GetResultCode());
m_bytesRead += bytesRead;
//if the read resulted in any size other than requested resize to the read size
if (response.m_data.size() != bytesRead)
{
response.m_data.resize(bytesRead);
AddBytesRead(connId, bytesRead, m_realtimeMetrics);
}
Send(connId, serial, response);
AddReadRequest(connId, m_realtimeMetrics);
if (m_realtimeMetrics)
{
Q_EMIT BytesSentChanged();
Q_EMIT NumReadRequestsChanged();
Q_EMIT BytesReceivedChanged();
Q_EMIT BytesReadChanged();
}
}
void FileServer::ProcessWriteRequest(unsigned int connId, unsigned int, unsigned int serial, QByteArray payload)
{
m_numWriteRequests++;
FileWriteRequest request;
if (!Recv(connId, payload, request))
{
FileWriteResponse response(static_cast<uint32_t>(ResultCode::Error), 0);
Send(connId, serial, response);
return;
}
AZ::IO::HandleType fileHandle = request.m_fileHandle;
AZ::u64 bytesWritten = 0;
auto fileIO = m_fileIOs[connId];
AZStd::string moreInfo = AZStd::string::format("%zu bytes", request.m_data.size());
RecordFileOp(fileIO.get(), "WRITE", fileHandle, moreInfo.c_str());
AZ::IO::Result res = fileIO->Write(fileHandle, request.m_data.data(), static_cast<uint64_t>(request.m_data.size()), &bytesWritten);
uint32_t resultCode = static_cast<uint32_t>(res.GetResultCode());
if (res)
{
m_bytesWritten += bytesWritten;
AddBytesWritten(connId, bytesWritten, m_realtimeMetrics);
}
// 0 serial means the other side doesn't care about the result
if (serial != 0)
{
FileWriteResponse response(resultCode, bytesWritten);
Send(connId, serial, response);
AddWriteRequest(connId, m_realtimeMetrics);
}
if (m_realtimeMetrics)
{
Q_EMIT BytesSentChanged();
Q_EMIT NumWriteRequestsChanged();
Q_EMIT BytesReceivedChanged();
Q_EMIT BytesWrittenChanged();
}
}
void FileServer::ProcessTellRequest(unsigned int connId, unsigned int, unsigned int serial, QByteArray payload)
{
m_numTellRequests++;
FileTellRequest request;
if (!Recv(connId, payload, request))
{
FileTellResponse response(static_cast<AZ::u32>(ResultCode::Error), 0);
Send(connId, serial, response);
return;
}
AZ::IO::HandleType fileHandle = request.m_fileHandle;
AZ::u64 offset = 0;
auto fileIO = m_fileIOs[connId];
AZStd::string moreInfo = AZStd::string::format("offset: %llu", offset);
RecordFileOp(fileIO.get(), "TELL", fileHandle, moreInfo.c_str());
AZ::IO::Result res = fileIO->Tell(fileHandle, offset);
uint32_t resultCode = static_cast<uint32_t>(res.GetResultCode());
FileTellResponse response(resultCode, offset);
Send(connId, serial, response);
AddTellRequest(connId, m_realtimeMetrics);
if (m_realtimeMetrics)
{
Q_EMIT BytesSentChanged();
Q_EMIT NumTellRequestsChanged();
Q_EMIT BytesReceivedChanged();
}
}
void FileServer::ProcessSeekRequest(unsigned int connId, unsigned int, unsigned int serial, QByteArray payload)
{
m_numSeekRequests++;
FileSeekRequest request;
if (!Recv(connId, payload, request))
{
FileSeekResponse response(static_cast<AZ::u32>(ResultCode::Error));
Send(connId, serial, response);
return;
}
AZ::IO::HandleType fileHandle = request.m_fileHandle;
AZ::IO::SeekType seekType = static_cast<AZ::IO::SeekType>(request.m_seekMode);
int64_t offset = request.m_offset;
auto fileIO = m_fileIOs[connId];
AZStd::string moreInfo = AZStd::string::format("offset: %lld, mode: %d", static_cast<AZ::s64>(offset), static_cast<AZ::u32>(seekType));
RecordFileOp(fileIO.get(), "SEEK", fileHandle, moreInfo.c_str());
AZ::IO::Result res = fileIO->Seek(fileHandle, offset, seekType);
uint32_t resultCode = static_cast<uint32_t>(res.GetResultCode());
FileSeekResponse response(resultCode);
Send(connId, serial, response);
AddSeekRequest(connId, m_realtimeMetrics);
if (m_realtimeMetrics)
{
Q_EMIT BytesSentChanged();
Q_EMIT NumSeekRequestsChanged();
Q_EMIT BytesReceivedChanged();
}
}
void FileServer::ProcessIsReadOnlyRequest(unsigned int connId, unsigned int, unsigned int serial, QByteArray payload)
{
EnsureCacheFolderExists(connId);
m_numIsReadOnlyRequests++;
FileIsReadOnlyRequest request;
if (!Recv(connId, payload, request))
{
FileIsReadOnlyResponse response(false);
Send(connId, serial, response);
return;
}
const char* filePath = request.m_filePath.c_str();
auto fileIO = m_fileIOs[connId];
RecordFileOp(fileIO.get(), "ISREADONLY", filePath, nullptr);
bool isReadOnly = fileIO->IsReadOnly(filePath);
FileIsReadOnlyResponse response(isReadOnly);
Send(connId, serial, response);
AddIsReadOnlyRequest(connId, m_realtimeMetrics);
if (m_realtimeMetrics)
{
Q_EMIT BytesSentChanged();
Q_EMIT NumIsReadOnlyRequestsChanged();
Q_EMIT BytesReceivedChanged();
}
}
void FileServer::ProcessIsDirectoryRequest(unsigned int connId, unsigned int, unsigned int serial, QByteArray payload)
{
EnsureCacheFolderExists(connId);
m_numIsDirectoryRequests++;
PathIsDirectoryRequest request;
if (!Recv(connId, payload, request))
{
PathIsDirectoryResponse response(false);
Send(connId, serial, response);
return;
}
const char* filePath = request.m_path.c_str();
auto fileIO = m_fileIOs[connId];
RecordFileOp(fileIO.get(), "ISDIR", filePath, nullptr);
bool isDirectory = fileIO->IsDirectory(filePath);
PathIsDirectoryResponse response(isDirectory);
Send(connId, serial, response);
AddIsDirectoryRequest(connId, m_realtimeMetrics);
if (m_realtimeMetrics)
{
Q_EMIT BytesSentChanged();
Q_EMIT NumIsDirectoryRequestsChanged();
Q_EMIT BytesReceivedChanged();
}
}
void FileServer::ProcessSizeRequest(unsigned int connId, unsigned int, unsigned int serial, QByteArray payload)
{
EnsureCacheFolderExists(connId);
m_numSizeRequests++;
FileSizeRequest request;
if (!Recv(connId, payload, request))
{
FileSizeResponse response(static_cast<AZ::u32>(ResultCode::Error), 0);
Send(connId, serial, response);
return;
}
const char* filePath = request.m_filePath.c_str();
AZ::u64 size = 0;
auto fileIO = m_fileIOs[connId];
RecordFileOp(fileIO.get(), "SIZE", filePath, nullptr);
AZ::IO::Result res = fileIO->Size(filePath, size);
uint32_t resultCode = static_cast<uint32_t>(res.GetResultCode());
FileSizeResponse response(resultCode, size);
Send(connId, serial, response);
AddSizeRequest(connId, m_realtimeMetrics);
if (m_realtimeMetrics)
{
Q_EMIT BytesSentChanged();
Q_EMIT NumSizeRequestsChanged();
Q_EMIT BytesReceivedChanged();
}
}
void FileServer::ProcessModificationTimeRequest(unsigned int connId, unsigned int, unsigned int serial, QByteArray payload)
{
EnsureCacheFolderExists(connId);
m_numModificationTimeRequests++;
FileModTimeRequest request;
if (!Recv(connId, payload, request))
{
FileModTimeResponse response(0);
Send(connId, serial, response);
return;
}
const char* filePath = request.m_filePath.c_str();
auto fileIO = m_fileIOs[connId];
RecordFileOp(fileIO.get(), "MODTIME", filePath, nullptr);
uint64_t modTime = fileIO->ModificationTime(filePath);
FileModTimeResponse response(modTime);
Send(connId, serial, response);
AddModificationTimeRequest(connId, m_realtimeMetrics);
if (m_realtimeMetrics)
{
Q_EMIT BytesSentChanged();
Q_EMIT NumModificationTimeRequestsChanged();
Q_EMIT BytesReceivedChanged();
}
}
void FileServer::ProcessExistsRequest(unsigned int connId, unsigned int, unsigned int serial, QByteArray payload)
{
EnsureCacheFolderExists(connId);
m_numExistsRequests++;
FileExistsRequest request;
if (!Recv(connId, payload, request))
{
FileExistsResponse response(false);
Send(connId, serial, response);
return;
}
const char* filePath = request.m_filePath.c_str();
auto fileIO = m_fileIOs[connId];
RecordFileOp(fileIO.get(), "EXISTS", filePath, nullptr);
bool exists = fileIO->Exists(filePath);
FileExistsResponse response(exists);
Send(connId, serial, response);
AddExistsRequest(connId, m_realtimeMetrics);
if (m_realtimeMetrics)
{
Q_EMIT BytesSentChanged();
Q_EMIT NumExistsRequestsChanged();
Q_EMIT BytesReceivedChanged();
}
}
void FileServer::ProcessFlushRequest(unsigned int connId, unsigned int, unsigned int serial, QByteArray payload)
{
m_numFlushRequests++;
FileFlushRequest request;
if (!Recv(connId, payload, request))
{
if (serial != 0)
{
FileFlushResponse response(static_cast<AZ::u32>(ResultCode::Error));
Send(connId, serial, response);
}
return;
}
AZ::IO::HandleType fileHandle = request.m_fileHandle;
auto fileIO = m_fileIOs[connId];
RecordFileOp(fileIO.get(), "FLUSH", fileHandle, nullptr);
AZ::IO::Result res = fileIO->Flush(fileHandle);
uint32_t resultCode = static_cast<uint32_t>(res.GetResultCode());
if (serial != 0)
{
FileFlushResponse response(resultCode);
Send(connId, serial, response);
}
AddFlushRequest(connId, m_realtimeMetrics);
if (m_realtimeMetrics)
{
Q_EMIT BytesSentChanged();
Q_EMIT NumFlushRequestsChanged();
Q_EMIT BytesReceivedChanged();
}
}
void FileServer::ProcessCreatePathRequest(unsigned int connId, unsigned int, unsigned int serial, QByteArray payload)
{
EnsureCacheFolderExists(connId);
m_numCreatePathRequests++;
PathCreateRequest request;
if (!Recv(connId, payload, request))
{
PathCreateResponse response(static_cast<AZ::u32>(ResultCode::Error));
Send(connId, serial, response);
return;
}
const char* filePath = request.m_path.c_str();
auto fileIO = m_fileIOs[connId];
RecordFileOp(fileIO.get(), "CREATEPATH", filePath, nullptr);
AZ::IO::Result res = fileIO->CreatePath(filePath);
uint32_t resultCode = static_cast<uint32_t>(res.GetResultCode());
PathCreateResponse response(resultCode);
Send(connId, serial, response);
AddCreatePathRequest(connId, m_realtimeMetrics);
if (m_realtimeMetrics)
{
Q_EMIT BytesSentChanged();
Q_EMIT NumCreatePathRequestsChanged();
Q_EMIT BytesReceivedChanged();
}
}
void FileServer::ProcessDestroyPathRequest(unsigned int connId, unsigned int, unsigned int serial, QByteArray payload)
{
EnsureCacheFolderExists(connId);
m_numDestroyPathRequests++;
PathDestroyRequest request;
if (!Recv(connId, payload, request))
{
PathDestroyResponse response(static_cast<AZ::u32>(ResultCode::Error));
Send(connId, serial, response);
return;
}
const char* filePath = request.m_path.c_str();
auto fileIO = m_fileIOs[connId];
RecordFileOp(fileIO.get(), "DESTROYPATH", filePath, nullptr);
AZ::IO::Result res = fileIO->DestroyPath(filePath);
uint32_t resultCode = static_cast<uint32_t>(res.GetResultCode());
PathDestroyResponse response(resultCode);
Send(connId, serial, response);
AddDestroyPathRequest(connId, m_realtimeMetrics);
if (m_realtimeMetrics)
{
Q_EMIT BytesSentChanged();
Q_EMIT NumDestroyPathRequestsChanged();
Q_EMIT BytesReceivedChanged();
}
}
void FileServer::ProcessRemoveRequest(unsigned int connId, unsigned int, unsigned int serial, QByteArray payload)
{
EnsureCacheFolderExists(connId);
m_numRemoveRequests++;
FileRemoveRequest request;
if (!Recv(connId, payload, request))
{
FileRemoveResponse response(static_cast<AZ::u32>(ResultCode::Error));
Send(connId, serial, response);
return;
}
const char* filePath = request.m_filePath.c_str();
auto fileIO = m_fileIOs[connId];
RecordFileOp(fileIO.get(), "REMOVE", filePath, nullptr);
AZ::IO::Result res = fileIO->Remove(filePath);
uint32_t resultCode = static_cast<uint32_t>(res.GetResultCode());
FileRemoveResponse response(resultCode);
Send(connId, serial, response);
AddRemoveRequest(connId, m_realtimeMetrics);
if (m_realtimeMetrics)
{
Q_EMIT BytesSentChanged();
Q_EMIT NumRemoveRequestsChanged();
Q_EMIT BytesReceivedChanged();
}
}
void FileServer::ProcessCopyRequest(unsigned int connId, unsigned int, unsigned int serial, QByteArray payload)
{
EnsureCacheFolderExists(connId);
m_numCopyRequests++;
FileCopyRequest request;
if (!Recv(connId, payload, request))
{
FileCopyResponse response(static_cast<AZ::u32>(ResultCode::Error));
Send(connId, serial, response);
return;
}
const char* sourcePath = request.m_srcPath.c_str();
const char* destinationPath = request.m_destPath.c_str();
auto fileIO = m_fileIOs[connId];
RecordFileOp(fileIO.get(), "COPY", sourcePath, destinationPath, nullptr);
AZ::IO::Result res = fileIO->Copy(sourcePath, destinationPath);
uint32_t resultCode = static_cast<uint32_t>(res.GetResultCode());
FileCopyResponse response(resultCode);
Send(connId, serial, response);
AddCopyRequest(connId, m_realtimeMetrics);
if (m_realtimeMetrics)
{
Q_EMIT BytesSentChanged();
Q_EMIT NumCopyRequestsChanged();
Q_EMIT BytesReceivedChanged();
}
}
void FileServer::ProcessRenameRequest(unsigned int connId, unsigned int, unsigned int serial, QByteArray payload)
{
EnsureCacheFolderExists(connId);
m_numRenameRequests++;
FileRenameRequest request;
if (!Recv(connId, payload, request))
{
FileRenameResponse response(static_cast<AZ::u32>(ResultCode::Error));
Send(connId, serial, response);
return;
}
const char* sourcePath = request.m_srcPath.c_str();
const char* destinationPath = request.m_destPath.c_str();
auto fileIO = m_fileIOs[connId];
RecordFileOp(fileIO.get(), "RENAME", sourcePath, destinationPath, nullptr);
AZ::IO::Result res = fileIO->Rename(sourcePath, destinationPath);
uint32_t resultCode = static_cast<uint32_t>(res.GetResultCode());
FileRenameResponse response(resultCode);
Send(connId, serial, response);
AddRenameRequest(connId, m_realtimeMetrics);
if (m_realtimeMetrics)
{
Q_EMIT BytesSentChanged();
Q_EMIT NumRenameRequestsChanged();
Q_EMIT BytesReceivedChanged();
}
}
void FileServer::ProcessFindFileNamesRequest(unsigned int connId, unsigned int, unsigned int serial, QByteArray payload)
{
EnsureCacheFolderExists(connId);
m_numFindFileNamesRequests++;
FindFilesRequest request;
if (!Recv(connId, payload, request))
{
FindFilesResponse response(static_cast<AZ::u32>(ResultCode::Error));
Send(connId, serial, response);
return;
}
const char* filePath = request.m_path.c_str();
const char* filter = request.m_filter.c_str();
FindFilesResponse::FileList fileNames;
auto fileIO = m_fileIOs[connId];
AZStd::string moreInfo = AZStd::string::format("filter: %s", filter);
RecordFileOp(fileIO.get(), "FINDFILES", filePath, moreInfo.c_str());
AZ::IO::Result res = fileIO->FindFiles(filePath, filter,
[&fileNames](const char* fileName)
{
fileNames.push_back(fileName);
return true;
});
uint32_t resultCode = static_cast<uint32_t>(res.GetResultCode());
FindFilesResponse response(resultCode, fileNames);
Send(connId, serial, response);
AddFindFileNamesRequest(connId, m_realtimeMetrics);
if (m_realtimeMetrics)
{
Q_EMIT BytesSentChanged();
Q_EMIT NumFindFileNamesRequestsChanged();
Q_EMIT BytesReceivedChanged();
}
}
void FileServer::ProcessFileTreeRequest(unsigned int connId, unsigned int, unsigned int serial, QByteArray payload)
{
EnsureCacheFolderExists(connId);
FileTreeRequest request;
if (!Recv(connId, payload, request))
{
FileTreeResponse response(static_cast<AZ::u32>(ResultCode::Error));
Send(connId, serial, response);
return;
}
auto fileIO = m_fileIOs[connId];
FileTreeResponse::FileList files;
FileTreeResponse::FolderList folders;
AZStd::vector<AZ::OSString> untestedFolders;
if (fileIO->IsDirectory("@assets@"))
{
folders.push_back("@assets@");
untestedFolders.push_back("@assets@");
}
if (fileIO->IsDirectory("@usercache@"))
{
folders.push_back("@usercache@");
untestedFolders.push_back("@usercache@");
}
if (fileIO->IsDirectory("@user@"))
{
folders.push_back("@user@");
untestedFolders.push_back("@user@");
}
if (fileIO->IsDirectory("@log@"))
{
folders.push_back("@log@");
untestedFolders.push_back("@log@");
}
if (fileIO->IsDirectory("@root@"))
{
folders.push_back("@root@");
untestedFolders.push_back("@root@");
}
AZ::IO::Result res = ResultCode::Success;
while (untestedFolders.size() && res == ResultCode::Success)
{
AZ::OSString folderName = untestedFolders.back();
untestedFolders.pop_back();
res = fileIO->FindFiles(folderName.c_str(), "*",
[&](const char* fileName)
{
if (fileIO->IsDirectory(fileName))
{
folders.push_back(fileName);
untestedFolders.push_back(fileName);
}
else
{
files.push_back(fileName);
}
return true;
}
);
}
if (res == ResultCode::Error)
{
files.clear();
folders.clear();
}
uint32_t resultCode = static_cast<uint32_t>(res.GetResultCode());
FileTreeResponse response(resultCode, files, folders);
Send(connId, serial, response);
}
void FileServer::RecordFileOp(AZ::IO::FileIOBase* fileIO, const char* op, const AZ::IO::HandleType& fileHandle, const char* moreInfo)
{
(void)fileIO;
(void)op;
(void)fileHandle;
(void)moreInfo;
#ifdef VERBOSE_FILE_OPS
char filename[MAX_PATH];
if (fileIO->GetFilename(fileHandle, filename, sizeof(filename)))
{
RecordFileOp(fileIO, op, filename, moreInfo);
}
#endif
}
void FileServer::RecordFileOp(AZ::IO::FileIOBase* fileIO, const char* op, const char* filePath, const char* moreInfo)
{
(void)fileIO;
(void)op;
(void)filePath;
(void)moreInfo;
#ifdef VERBOSE_FILE_OPS
AZ_TracePrintf(AssetProcessor::DebugChannel, "FileServer Operation : %s, filePath : %s, moreInfo: %s.\n", op, filePath, moreInfo ? moreInfo : "");
#endif
}
void FileServer::RecordFileOp(AZ::IO::FileIOBase* fileIO, const char* op, const char* sourceFile, const char* destFile, const char* moreInfo)
{
(void)fileIO;
(void)op;
(void)sourceFile;
(void)destFile;
(void)moreInfo;
#ifdef VERBOSE_FILE_OPS
AZ_TracePrintf(AssetProcessor::DebugChannel, "FileServer Operation : %s, sourceFile : %s, destFile : %s, moreInfo: %s.\n", op, sourceFile, destFile, moreInfo ? moreInfo : "");
#endif
}