Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
@@ -0,0 +1,921 @@
/*
* 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 "connection.h"
#include "native/connection/connectionworker.h"
#include "native/utilities/ByteArrayStream.h"
#include <QSettings>
Connection::Connection(qintptr socketDescriptor, QObject* parent)
: Connection(false, socketDescriptor, parent)
{
}
Connection::Connection(bool isUserCreatedConnection, qintptr socketDescriptor, QObject* parent)
: QObject(parent)
, m_userCreatedConnection(isUserCreatedConnection)
{
m_runElapsed = true;
//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_numEofRequests = 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;
//connection
m_identifier = "";//empty
m_ipAddress = "127.0.0.1";// default is loopback address
m_port = 22229;//default port number
m_status = Disconnected;//default status
m_autoConnect = false;//default status
m_connectionId = 0; //default
m_connectionWorker = new AssetProcessor::ConnectionWorker(socketDescriptor);
m_connectionWorker->moveToThread(&m_connectionWorkerThread);
m_connectionWorker->GetSocket().moveToThread(&m_connectionWorkerThread);
connect(this, &Connection::TerminateConnection, m_connectionWorker, &AssetProcessor::ConnectionWorker::RequestTerminate, Qt::DirectConnection);
connect(this, &Connection::NormalConnectionRequested, m_connectionWorker, &AssetProcessor::ConnectionWorker::ConnectToEngine);
connect(m_connectionWorker, &AssetProcessor::ConnectionWorker::Identifier, this, [this](QString identifier) {
// For user created connections, the id is user generated (either because they've manually entered some text
// this session, or because the id was loaded from a session previously saved where the user entered it).
// As such, when the connection worker reports a new id from after the connection occurs,
// we only pay attention to it when it is not a user created connection.
if (!m_userCreatedConnection)
{
SetIdentifier(identifier);
}
});
connect(m_connectionWorker, &AssetProcessor::ConnectionWorker::AssetPlatformsString, this, &Connection::SetAssetPlatformsString);
connect(m_connectionWorker, &AssetProcessor::ConnectionWorker::ConnectionDisconnected, this, &Connection::OnConnectionDisconnect, Qt::QueuedConnection);
// the blocking queued connection is here because the worker calls OnConnectionEstablished and then immediately starts emitting messages about
// data coming in. We want to immediately establish connectivity this way and we don't want it to proceed with message delivery until then.
connect(m_connectionWorker, &AssetProcessor::ConnectionWorker::ConnectionEstablished, this, &Connection::OnConnectionEstablished, Qt::BlockingQueuedConnection);
connect(m_connectionWorker, &AssetProcessor::ConnectionWorker::ErrorMessage, this, &Connection::ErrorMessage);
connect(m_connectionWorker, &AssetProcessor::ConnectionWorker::IsAddressWhiteListed, this, &Connection::IsAddressWhiteListed);
connect(this, &Connection::AddressIsWhiteListed, m_connectionWorker, &AssetProcessor::ConnectionWorker::AddressIsWhiteListed);
}
void Connection::Activate(qintptr socketDescriptor)
{
m_connectionWorkerThread.setObjectName("Connection Worker Thread");
m_connectionWorkerThread.start();
//if socketDescriptor is positive it means that it is an incoming connection
if (socketDescriptor >= 0)
{
SetStatus(Connecting);
// by invoking the ConnectSocket, we cause it to occur in the worker's thread
QMetaObject::invokeMethod(m_connectionWorker, "ConnectSocket", Q_ARG(qintptr, socketDescriptor));
}
}
Connection::~Connection()
{
Q_ASSERT(!m_connectionWorkerThread.isRunning());
Q_EMIT ConnectionDestroyed(m_connectionId);
}
QString Connection::Identifier() const
{
return m_identifier;
}
void Connection::SetIdentifier(QString identifier)
{
if (m_identifier == identifier)
{
return;
}
m_identifier = identifier;
Q_EMIT IdentifierChanged();
Q_EMIT DisplayNameChanged(); // regardless of whether the identifier is empty or not, this always affects the display name.
}
QString Connection::IpAddress() const
{
return m_ipAddress;
}
QStringList Connection::AssetPlatforms() const
{
return m_assetPlatforms;
}
QString Connection::AssetPlatformsString() const
{
return m_assetPlatforms.join(',');
}
void Connection::SetAssetPlatforms(QStringList assetPlatforms)
{
if (m_assetPlatforms == assetPlatforms)
{
return;
}
m_assetPlatforms = assetPlatforms;
Q_EMIT AssetPlatformChanged();
}
QString Connection::DisplayName() const
{
if (m_identifier.isEmpty())
{
return m_ipAddress;
}
return m_identifier;
}
QString Connection::Elapsed() const
{
return m_elapsedDisplay;
}
void Connection::SetIpAddress(QString ipAddress)
{
if (Status() == Connected)
{
AZ_Warning(AssetProcessor::ConsoleChannel, Status() == Connected, "You are not allowed to change the ip address of a connected connection.\n");
return;
}
if (ipAddress == m_ipAddress)
{
return;
}
m_ipAddress = ipAddress;
Q_EMIT IpAddressChanged();
if (m_identifier.isEmpty()) // if the identifier is empty, then the display name is the ip address
{
Q_EMIT DisplayNameChanged();
}
}
int Connection::Port() const
{
return m_port;
}
void Connection::SetPort(int port)
{
if (Status() == Connected)
{
AZ_Warning(AssetProcessor::ConsoleChannel, Status() == Connected, "You are not allowed to change the port of a connected connection.\n");
return;
}
if (port == m_port)
{
return;
}
m_port = aznumeric_cast<quint16>(port);
Q_EMIT PortChanged();
}
Connection::ConnectionStatus Connection::Status() const
{
return m_status;
}
void Connection::SaveConnection(QSettings& qSettings)
{
qSettings.setValue("identifier", Identifier());
qSettings.setValue("ipAddress", IpAddress());
qSettings.setValue("port", Port());
qSettings.setValue("assetplatform", AssetPlatforms());
qSettings.setValue("autoConnect", AutoConnect());
qSettings.setValue("userConnection", m_userCreatedConnection);
}
void Connection::LoadConnection(QSettings& qSettings)
{
SetIdentifier(qSettings.value("identifier").toString());
SetIpAddress(qSettings.value("ipAddress").toString());
SetPort(qSettings.value("port").toInt());
SetAssetPlatformsString(qSettings.value("assetplatform").toString());
SetAutoConnect(qSettings.value("autoConnect").toBool());
SetStatus(Disconnected);
m_userCreatedConnection = qSettings.value("userConnection", false).toBool();
}
void Connection::SetStatus(Connection::ConnectionStatus status)
{
if (status == m_status)
{
return;
}
m_status = status;
Q_EMIT StatusChanged(m_connectionId);
if (status == Connection::Connected)
{
AssetProcessor::ConnectionBus::Handler::BusConnect(m_connectionId);
}
else if (status == Connection::Disconnected)
{
AssetProcessor::ConnectionBus::Handler::BusDisconnect();
}
}
bool Connection::AutoConnect() const
{
return m_autoConnect;
}
void Connection::Connect()
{
m_queuedReconnect = false;
if (!m_connectionWorker)
{
// this can happen if you queued a connect but in the interim, we were deleteLater'd due to removal.
return;
}
m_connectionWorker->Reset();
Q_EMIT NormalConnectionRequested(m_ipAddress, m_port);
}
void Connection::Disconnect()
{
Q_EMIT DisconnectConnection(m_connectionId);
}
void Connection::Terminate()
{
Q_EMIT TerminateConnection();
if (m_connectionWorkerThread.isRunning())
{
m_connectionWorkerThread.quit();
m_connectionWorkerThread.wait();
}
deleteLater();
}
void Connection::SetAutoConnect(bool autoConnect)
{
if (autoConnect == m_autoConnect)
{
return;
}
m_autoConnect = autoConnect;
if (m_autoConnect)
{
SetStatus(Connecting);
Connect();
}
else
{
SetStatus(Disconnected);
Disconnect();
}
Q_EMIT AutoConnectChanged();
}
void Connection::OnConnectionDisconnect()
{
if (m_connectionWorker)
{
disconnect(this, &Connection::SendMessage, m_connectionWorker, &AssetProcessor::ConnectionWorker::SendMessage);
disconnect(m_connectionWorker, &AssetProcessor::ConnectionWorker::ReceiveMessage, this, &Connection::ReceiveMessage);
}
// For user created connections, the id is user generated (either because they've manually entered some text
// this session, or because the id was loaded from a session previously saved where the user entered it).
// As such, when a connection disconnects, we only want to clear the id when the connection was triggered
// from something other than the user (i.e. like when an automatic connection from Editor or a job worker
// disconnects).
if (!m_userCreatedConnection)
{
SetIdentifier(QString());
}
SetAssetPlatforms(QStringList());
if (m_autoConnect)
{
if (!m_queuedReconnect)
{
m_queuedReconnect = true;
SetStatus(Connecting);
QTimer::singleShot(500, this, SLOT(Connect()));
}
}
else
{
Disconnect();
SetStatus(Disconnected);
SetAssetPlatforms(QStringList());
// if we did not initiate the connection, we should erase it when it disappears.
if (!InitiatedConnection())
{
Terminate();
}
}
}
void Connection::OnConnectionEstablished(QString ipAddress, quint16 port)
{
connect(this, &Connection::SendMessage, m_connectionWorker, &AssetProcessor::ConnectionWorker::SendMessage, Qt::UniqueConnection);
connect(m_connectionWorker, &AssetProcessor::ConnectionWorker::ReceiveMessage, this, &Connection::ReceiveMessage, Qt::UniqueConnection);
m_elapsed = 0;
m_elapsedTimer.start();
m_runElapsed = true;
UpdateElapsed();
SetIpAddress(ipAddress);
SetPort(port);
SetStatus(Connected);
}
void Connection::ReceiveMessage(unsigned int type, unsigned int serial, QByteArray payload)
{
Q_EMIT DeliverMessage(m_connectionId, type, serial, payload);
}
void Connection::ErrorMessage(QString errorString)
{
Q_EMIT Error(m_connectionId, errorString);
}
void Connection::UpdateElapsed()
{
if (m_runElapsed)
{
m_elapsed += m_elapsedTimer.restart();
int seconds = aznumeric_cast<int>(m_elapsed / 1000);
int hours = seconds / (60 * 60);
seconds -= hours * (60 * 60);
int minutes = seconds / 60;
seconds -= minutes * 60;
m_elapsedDisplay.clear();
if (hours < 10)
{
m_elapsedDisplay = "0";
}
m_elapsedDisplay += QString::number(hours) + ":";
if (minutes < 10)
{
m_elapsedDisplay += "0";
}
m_elapsedDisplay += QString::number(minutes) + ":";
if (seconds < 10)
{
m_elapsedDisplay += "0";
}
m_elapsedDisplay += QString::number(seconds);
Q_EMIT ElapsedChanged();
QTimer::singleShot(1000, this, SLOT(UpdateElapsed()));
}
}
unsigned int Connection::ConnectionId() const
{
return m_connectionId;
}
void Connection::SetConnectionId(unsigned int connectionId)
{
m_connectionId = connectionId;
}
void Connection::SendMessageToWorker(unsigned int type, unsigned int serial, QByteArray payload)
{
Q_EMIT SendMessage(type, serial, payload);
}
void Connection::AddBytesReceived(qint64 add, bool update)
{
m_bytesReceived += add;
if (update)
{
Q_EMIT BytesReceivedChanged();
}
}
void Connection::AddBytesSent(qint64 add, bool update)
{
m_bytesSent += add;
if (update)
{
Q_EMIT BytesSentChanged();
}
}
void Connection::AddBytesRead(qint64 add, bool update)
{
m_bytesRead += add;
if (update)
{
Q_EMIT BytesReadChanged();
}
}
void Connection::AddBytesWritten(qint64 add, bool update)
{
m_bytesWritten += add;
if (update)
{
Q_EMIT BytesWrittenChanged();
}
}
void Connection::AddOpenRequest(bool update)
{
m_numOpenRequests++;
if (update)
{
Q_EMIT NumOpenRequestsChanged();
}
}
void Connection::AddCloseRequest(bool update)
{
m_numCloseRequests++;
if (update)
{
Q_EMIT NumCloseRequestsChanged();
}
}
void Connection::AddOpened(bool update)
{
m_numOpened++;
m_numOpenFiles = m_numOpened - m_numClosed;
if (update)
{
Q_EMIT NumOpenedChanged();
Q_EMIT NumOpenFilesChanged();
}
}
void Connection::AddClosed(bool update)
{
m_numClosed++;
m_numOpenFiles = m_numOpened - m_numClosed;
if (update)
{
Q_EMIT NumClosedChanged();
Q_EMIT NumOpenFilesChanged();
}
}
void Connection::AddReadRequest(bool update)
{
m_numReadRequests++;
if (update)
{
Q_EMIT NumReadRequestsChanged();
}
}
void Connection::AddWriteRequest(bool update)
{
m_numWriteRequests++;
if (update)
{
Q_EMIT NumWriteRequestsChanged();
}
}
void Connection::AddTellRequest(bool update)
{
m_numTellRequests++;
if (update)
{
Q_EMIT NumTellRequestsChanged();
}
}
void Connection::AddSeekRequest(bool update)
{
m_numSeekRequests++;
if (update)
{
Q_EMIT NumSeekRequestsChanged();
}
}
void Connection::AddEofRequest(bool update)
{
m_numEofRequests++;
if (update)
{
Q_EMIT NumEofRequestsChanged();
}
}
void Connection::AddIsReadOnlyRequest(bool update)
{
m_numIsReadOnlyRequests++;
if (update)
{
Q_EMIT NumIsReadOnlyRequestsChanged();
}
}
void Connection::AddIsDirectoryRequest(bool update)
{
m_numIsDirectoryRequests++;
if (update)
{
Q_EMIT NumIsDirectoryRequestsChanged();
}
}
void Connection::AddSizeRequest(bool update)
{
m_numSizeRequests++;
if (update)
{
Q_EMIT NumSizeRequestsChanged();
}
}
void Connection::AddModificationTimeRequest(bool update)
{
m_numModificationTimeRequests++;
if (update)
{
Q_EMIT NumModificationTimeRequestsChanged();
}
}
void Connection::AddExistsRequest(bool update)
{
m_numExistsRequests++;
if (update)
{
Q_EMIT NumExistsRequestsChanged();
}
}
void Connection::AddFlushRequest(bool update)
{
m_numFlushRequests++;
if (update)
{
Q_EMIT NumFlushRequestsChanged();
}
}
void Connection::AddCreatePathRequest(bool update)
{
m_numCreatePathRequests++;
if (update)
{
Q_EMIT NumCreatePathRequestsChanged();
}
}
void Connection::AddDestroyPathRequest(bool update)
{
m_numDestroyPathRequests++;
if (update)
{
Q_EMIT NumDestroyPathRequestsChanged();
}
}
void Connection::AddRemoveRequest(bool update)
{
m_numRemoveRequests++;
if (update)
{
Q_EMIT NumRemoveRequestsChanged();
}
}
void Connection::AddCopyRequest(bool update)
{
m_numCopyRequests++;
if (update)
{
Q_EMIT NumCopyRequestsChanged();
}
}
void Connection::AddRenameRequest(bool update)
{
m_numRenameRequests++;
if (update)
{
Q_EMIT NumRenameRequestsChanged();
}
}
void Connection::AddFindFileNamesRequest(bool update)
{
m_numFindFileNamesRequests++;
if (update)
{
Q_EMIT NumFindFileNamesRequestsChanged();
}
}
void Connection::UpdateBytesReceived()
{
Q_EMIT BytesReceivedChanged();
}
void Connection::UpdateBytesSent()
{
Q_EMIT BytesSentChanged();
}
void Connection::UpdateBytesRead()
{
Q_EMIT BytesReadChanged();
}
void Connection::UpdateBytesWritten()
{
Q_EMIT BytesWrittenChanged();
}
void Connection::UpdateOpenRequest()
{
Q_EMIT NumOpenRequestsChanged();
}
void Connection::UpdateCloseRequest()
{
Q_EMIT NumCloseRequestsChanged();
}
void Connection::UpdateOpened()
{
Q_EMIT NumOpenedChanged();
}
void Connection::UpdateClosed()
{
Q_EMIT NumClosedChanged();
}
void Connection::UpdateReadRequest()
{
Q_EMIT NumReadRequestsChanged();
}
void Connection::UpdateWriteRequest()
{
Q_EMIT NumWriteRequestsChanged();
}
void Connection::UpdateTellRequest()
{
Q_EMIT NumTellRequestsChanged();
}
void Connection::UpdateSeekRequest()
{
Q_EMIT NumSeekRequestsChanged();
}
void Connection::UpdateEofRequest()
{
Q_EMIT NumEofRequestsChanged();
}
void Connection::UpdateIsReadOnlyRequest()
{
Q_EMIT NumIsReadOnlyRequestsChanged();
}
void Connection::UpdateIsDirectoryRequest()
{
Q_EMIT NumIsDirectoryRequestsChanged();
}
void Connection::UpdateSizeRequest()
{
Q_EMIT NumSizeRequestsChanged();
}
void Connection::UpdateModificationTimeRequest()
{
Q_EMIT NumModificationTimeRequestsChanged();
}
void Connection::UpdateExistsRequest()
{
Q_EMIT NumExistsRequestsChanged();
}
void Connection::UpdateFlushRequest()
{
Q_EMIT NumFlushRequestsChanged();
}
void Connection::UpdateCreatePathRequest()
{
Q_EMIT NumCreatePathRequestsChanged();
}
void Connection::UpdateDestroyPathRequest()
{
Q_EMIT NumDestroyPathRequestsChanged();
}
void Connection::UpdateRemoveRequest()
{
Q_EMIT NumRemoveRequestsChanged();
}
void Connection::UpdateCopyRequest()
{
Q_EMIT NumCopyRequestsChanged();
}
void Connection::UpdateRenameRequest()
{
Q_EMIT NumRenameRequestsChanged();
}
void Connection::UpdateFindFileNamesRequest()
{
Q_EMIT NumFindFileNamesRequestsChanged();
}
void Connection::UpdateMetrics()
{
UpdateBytesReceived();
UpdateBytesSent();
UpdateBytesRead();
UpdateBytesWritten();
UpdateOpenRequest();
UpdateCloseRequest();
UpdateOpened();
UpdateClosed();
UpdateReadRequest();
UpdateWriteRequest();
UpdateTellRequest();
UpdateSeekRequest();
UpdateEofRequest();
UpdateIsReadOnlyRequest();
UpdateIsDirectoryRequest();
UpdateSizeRequest();
UpdateModificationTimeRequest();
UpdateExistsRequest();
UpdateFlushRequest();
UpdateCreatePathRequest();
UpdateDestroyPathRequest();
UpdateRemoveRequest();
UpdateCopyRequest();
UpdateRenameRequest();
UpdateFindFileNamesRequest();
}
size_t Connection::Send(unsigned int serial, const AzFramework::AssetSystem::BaseAssetProcessorMessage& message)
{
QByteArray buffer;
bool wroteToStream = AssetProcessor::PackMessage(message, buffer);
AZ_Assert(wroteToStream, "Connection::Send: Could not serialize to stream (type=%u)", message.GetMessageType());
if (wroteToStream)
{
return SendRaw(message.GetMessageType(), serial, buffer);
}
return 0;
}
size_t Connection::SendRaw(unsigned int type, unsigned int serial, const QByteArray& data)
{
SendMessageToWorker(type, serial, data);
return data.size();
}
size_t Connection::SendPerPlatform(unsigned int serial, const AzFramework::AssetSystem::BaseAssetProcessorMessage& message, const QString& platform)
{
if (m_assetPlatforms.contains(platform, Qt::CaseInsensitive))
{
return Send(serial, message);
}
return 0;
}
size_t Connection::SendRawPerPlatform(unsigned int type, unsigned int serial, const QByteArray& data, const QString& platform)
{
if (m_assetPlatforms.contains(platform, Qt::CaseInsensitive))
{
return SendRaw(type, serial, data);
}
return 0;
}
AZ::u32 Connection::GetNextSerial()
{
static AZStd::atomic_uint serial(AzFramework::AssetSystem::DEFAULT_SERIAL);
AZ::u32 nextSerial = ++serial;
// Avoid special-case serials
return (nextSerial & AzFramework::AssetSystem::RESPONSE_SERIAL_FLAG
|| nextSerial == AzFramework::AssetSystem::DEFAULT_SERIAL
|| nextSerial == AzFramework::AssetSystem::NEGOTIATION_SERIAL)
? GetNextSerial() // re-roll, we picked a special serial
: nextSerial;
}
unsigned int Connection::SendRequest(const AzFramework::AssetSystem::BaseAssetProcessorMessage& message, const AssetProcessor::ConnectionBusTraits::ResponseCallback& callback)
{
AZ::u32 serial = GetNextSerial();
{
AZStd::lock_guard<AZStd::mutex> lock(m_responseHandlerMutex);
m_responseHandlerMap.insert({ serial, callback });
}
Send(serial, message);
return serial;
}
size_t Connection::SendResponse(unsigned int serial, const AzFramework::AssetSystem::BaseAssetProcessorMessage& message)
{
serial |= AzFramework::AssetSystem::RESPONSE_SERIAL_FLAG; // Set top bit to indicate this is a response
return Send(serial, message);
}
void Connection::InvokeResponseHandler(AZ::u32 serial, AZ::u32 type, QByteArray data)
{
AZStd::lock_guard<AZStd::mutex> lock(m_responseHandlerMutex);
auto itr = m_responseHandlerMap.find(serial);
if (itr != m_responseHandlerMap.end())
{
itr->second(type, data);
m_responseHandlerMap.erase(itr);
}
}
void Connection::RemoveResponseHandler(unsigned int serial)
{
AZStd::lock_guard<AZStd::mutex> lock(m_responseHandlerMutex);
m_responseHandlerMap.erase(serial);
}
bool Connection::InitiatedConnection() const
{
if (m_connectionWorker)
{
return m_connectionWorker->InitiatedConnection();
}
return false;
}
bool Connection::UserCreatedConnection() const
{
return m_userCreatedConnection;
}
void Connection::SetAssetPlatformsString(QString assetPlatforms)
{
SetAssetPlatforms(assetPlatforms.split(',', Qt::SkipEmptyParts));
}
@@ -0,0 +1,306 @@
/*
* 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.
*
*/
#ifndef CONNECTION_H
#define CONNECTION_H
#if !defined(Q_MOC_RUN)
#include <QThread>
#include <QElapsedTimer>
#include "native/utilities/AssetUtilEBusHelper.h"
#include <QHostAddress>
#include <QTimer>
#include <QString>
#include <QPointer>
#endif
class QSettings;
namespace AssetProcessor
{
class ConnectionWorker;
class PlatformConfiguration;
}
#undef SendMessage
/** This Class contains all the information related to a single connecton
*/
class Connection
: public QObject
, public AssetProcessor::ConnectionBus::Handler
{
Q_OBJECT
Q_PROPERTY(QString identifier READ Identifier WRITE SetIdentifier NOTIFY IdentifierChanged)
Q_PROPERTY(QString ipAddress READ IpAddress WRITE SetIpAddress NOTIFY IpAddressChanged)
Q_PROPERTY(int port READ Port WRITE SetPort NOTIFY PortChanged)
Q_PROPERTY(ConnectionStatus status READ Status NOTIFY StatusChanged)
Q_PROPERTY(QStringList assetPlatform READ AssetPlatforms WRITE SetAssetPlatforms NOTIFY AssetPlatformChanged)
Q_PROPERTY(QString assetPlatformsString READ AssetPlatformsString WRITE SetAssetPlatformsString)
Q_PROPERTY(bool autoConnect READ AutoConnect WRITE SetAutoConnect NOTIFY AutoConnectChanged)
Q_PROPERTY(QString displayName READ DisplayName NOTIFY DisplayNameChanged)
Q_PROPERTY(QString elapsed READ Elapsed NOTIFY ElapsedChanged)
//metrics
Q_PROPERTY(qint64 numOpenRequests MEMBER m_numOpenRequests NOTIFY NumOpenRequestsChanged)
Q_PROPERTY(qint64 numCloseRequests MEMBER m_numCloseRequests NOTIFY NumCloseRequestsChanged)
Q_PROPERTY(qint64 numOpened MEMBER m_numOpened NOTIFY NumOpenedChanged)
Q_PROPERTY(qint64 numClosed MEMBER m_numClosed NOTIFY NumClosedChanged)
Q_PROPERTY(qint64 numReadRequests MEMBER m_numReadRequests NOTIFY NumReadRequestsChanged)
Q_PROPERTY(qint64 numWriteRequests MEMBER m_numWriteRequests NOTIFY NumWriteRequestsChanged)
Q_PROPERTY(qint64 numSeekRequests MEMBER m_numSeekRequests NOTIFY NumSeekRequestsChanged)
Q_PROPERTY(qint64 numTellRequests MEMBER m_numTellRequests NOTIFY NumTellRequestsChanged)
Q_PROPERTY(qint64 numEofRequests MEMBER m_numEofRequests NOTIFY NumEofRequestsChanged)
Q_PROPERTY(qint64 numIsReadOnlyRequests MEMBER m_numIsReadOnlyRequests NOTIFY NumIsReadOnlyRequestsChanged)
Q_PROPERTY(qint64 numIsDirectoryRequests MEMBER m_numIsDirectoryRequests NOTIFY NumIsDirectoryRequestsChanged)
Q_PROPERTY(qint64 numSizeRequests MEMBER m_numSizeRequests NOTIFY NumSizeRequestsChanged)
Q_PROPERTY(qint64 numModificationTimeRequests MEMBER m_numModificationTimeRequests NOTIFY NumModificationTimeRequestsChanged)
Q_PROPERTY(qint64 numExistsRequests MEMBER m_numExistsRequests NOTIFY NumExistsRequestsChanged)
Q_PROPERTY(qint64 numFlushRequests MEMBER m_numFlushRequests NOTIFY NumFlushRequestsChanged)
Q_PROPERTY(qint64 numCreatePathRequests MEMBER m_numCreatePathRequests NOTIFY NumCreatePathRequestsChanged)
Q_PROPERTY(qint64 numDestroyPathRequests MEMBER m_numDestroyPathRequests NOTIFY NumDestroyPathRequestsChanged)
Q_PROPERTY(qint64 numRemoveRequests MEMBER m_numRemoveRequests NOTIFY NumRemoveRequestsChanged)
Q_PROPERTY(qint64 numCopyRequests MEMBER m_numCopyRequests NOTIFY NumCopyRequestsChanged)
Q_PROPERTY(qint64 numRenameRequests MEMBER m_numRenameRequests NOTIFY NumRenameRequestsChanged)
Q_PROPERTY(qint64 numFindFileNamesRequests MEMBER m_numFindFileNamesRequests NOTIFY NumFindFileNamesRequestsChanged)
Q_PROPERTY(qint64 bytesRead MEMBER m_bytesRead NOTIFY BytesReadChanged)
Q_PROPERTY(qint64 bytesWritten MEMBER m_bytesWritten NOTIFY BytesWrittenChanged)
Q_PROPERTY(qint64 bytesSent MEMBER m_bytesSent NOTIFY BytesSentChanged)
Q_PROPERTY(qint64 bytesReceived MEMBER m_bytesReceived NOTIFY BytesReceivedChanged)
Q_PROPERTY(qint64 numOpenFiles MEMBER m_numOpenFiles NOTIFY NumOpenFilesChanged)
public:
explicit Connection(qintptr socketDescriptor = -1, QObject* parent = 0);
explicit Connection(bool isUserCreatedConnection, qintptr socketDescriptor = -1, QObject* parent = 0);
virtual ~Connection();
enum ConnectionStatus
{
Disconnected, Connected, Connecting
};
Q_ENUMS(ConnectionStatus)
void Activate(qintptr socketDescriptor);
QString Identifier() const;
QString IpAddress() const;
int Port() const;
ConnectionStatus Status() const;
QStringList AssetPlatforms() const;
QString AssetPlatformsString() const;
void SaveConnection(QSettings& qSettings);
void LoadConnection(QSettings& qSettings);
bool AutoConnect() const;
QString DisplayName() const;
QString Elapsed() const;
bool InitiatedConnection() const;
bool UserCreatedConnection() const;
void Disconnect();
unsigned int ConnectionId() const;
void SetConnectionId(unsigned int ConnectionId);
void Terminate();
void SendMessageToWorker(unsigned int type, unsigned int serial, QByteArray payload);
void AddBytesReceived(qint64 add, bool update);
void AddBytesSent(qint64 add, bool update);
void AddBytesRead(qint64 add, bool update);
void AddBytesWritten(qint64 add, bool update);
void AddOpenRequest(bool update);
void AddCloseRequest(bool update);
void AddOpened(bool update);
void AddClosed(bool update);
void AddReadRequest(bool update);
void AddWriteRequest(bool update);
void AddTellRequest(bool update);
void AddSeekRequest(bool update);
void AddEofRequest(bool update);
void AddIsReadOnlyRequest(bool update);
void AddIsDirectoryRequest(bool update);
void AddSizeRequest(bool update);
void AddModificationTimeRequest(bool update);
void AddExistsRequest(bool update);
void AddFlushRequest(bool update);
void AddCreatePathRequest(bool update);
void AddDestroyPathRequest(bool update);
void AddRemoveRequest(bool update);
void AddCopyRequest(bool update);
void AddRenameRequest(bool update);
void AddFindFileNamesRequest(bool update);
void UpdateBytesReceived();
void UpdateBytesSent();
void UpdateBytesRead();
void UpdateBytesWritten();
void UpdateOpenRequest();
void UpdateCloseRequest();
void UpdateOpened();
void UpdateClosed();
void UpdateReadRequest();
void UpdateWriteRequest();
void UpdateTellRequest();
void UpdateSeekRequest();
void UpdateEofRequest();
void UpdateIsReadOnlyRequest();
void UpdateIsDirectoryRequest();
void UpdateSizeRequest();
void UpdateModificationTimeRequest();
void UpdateExistsRequest();
void UpdateFlushRequest();
void UpdateCreatePathRequest();
void UpdateDestroyPathRequest();
void UpdateRemoveRequest();
void UpdateCopyRequest();
void UpdateRenameRequest();
void UpdateFindFileNamesRequest();
void UpdateMetrics();
// AssetProcessor::ConnectionBus interface
size_t Send(unsigned int serial, const AzFramework::AssetSystem::BaseAssetProcessorMessage& message) override;
size_t SendRaw(unsigned int type, unsigned int serial, const QByteArray& data) override;
size_t SendPerPlatform(unsigned int serial, const AzFramework::AssetSystem::BaseAssetProcessorMessage& message, const QString& platform) override;
size_t SendRawPerPlatform(unsigned int type, unsigned int serial, const QByteArray& data, const QString& platform) override;
//! callback runs on the main thread, be sure to keep the work to an absolute minimum
unsigned int SendRequest(const AzFramework::AssetSystem::BaseAssetProcessorMessage& message, const AssetProcessor::ConnectionBusTraits::ResponseCallback& callback) override;
size_t SendResponse(unsigned int serial, const AzFramework::AssetSystem::BaseAssetProcessorMessage& message) override;
void RemoveResponseHandler(unsigned int serial) override;
void InvokeResponseHandler(AZ::u32 serial, AZ::u32 type, QByteArray data);
Q_SIGNALS:
void IdentifierChanged();
void IpAddressChanged();
void PortChanged();
void StatusChanged(unsigned int connId);
void AssetPlatformChanged();
void AutoConnectChanged();
void DisplayNameChanged();
void ElapsedChanged();
void NormalConnectionRequested(QString IpAddress, quint16 Port);
void connectionEnded();
void TerminateConnection();
void SendMessage(unsigned int type, unsigned int serial, QByteArray payload);
void DeliverMessage(unsigned int connId, unsigned int type, unsigned int serial, QByteArray payload);
void ConnectionDestroyed(unsigned int connId);
void DisconnectConnection(unsigned int connId);
void AddGameMessageToOutgoingQueue();
void Error(unsigned int connId, QString errorString);
// the token is just any identifier to identify a particular connection, potentially from the same host.
// the response (AddressIsWhiteListed) will have the same token as was sent.
void IsAddressWhiteListed(QHostAddress hostAddress, void* token);
void AddressIsWhiteListed(void* token, bool result);
//metrics
void NumOpenRequestsChanged();
void NumCloseRequestsChanged();
void NumOpenedChanged();
void NumClosedChanged();
void NumReadRequestsChanged();
void NumWriteRequestsChanged();
void NumSeekRequestsChanged();
void NumTellRequestsChanged();
void NumEofRequestsChanged();
void NumIsReadOnlyRequestsChanged();
void NumIsDirectoryRequestsChanged();
void NumSizeRequestsChanged();
void NumModificationTimeRequestsChanged();
void NumExistsRequestsChanged();
void NumFlushRequestsChanged();
void NumCreatePathRequestsChanged();
void NumDestroyPathRequestsChanged();
void NumRemoveRequestsChanged();
void NumCopyRequestsChanged();
void NumRenameRequestsChanged();
void NumFindFileNamesRequestsChanged();
void BytesReadChanged();
void BytesWrittenChanged();
void BytesSentChanged();
void BytesReceivedChanged();
void NumOpenFilesChanged();
public Q_SLOTS:
void SetIdentifier(QString Identifier);
void SetIpAddress(QString IpAddress);
void SetPort(int Port);
void SetStatus(ConnectionStatus Status);
void SetAssetPlatforms(QStringList assetPlatform);
void SetAssetPlatformsString(QString assetPlatforms);
void SetAutoConnect(bool AutoConnect);
void OnConnectionDisconnect();
void OnConnectionEstablished(QString ipAddress, quint16 port);
void ReceiveMessage(unsigned int type, unsigned int serial, QByteArray payload);
void ErrorMessage(QString negotiateFailure);
void UpdateElapsed();
void Connect();
private:
AZ::u32 GetNextSerial();
unsigned int m_connectionId;
QString m_identifier;
QString m_ipAddress;
quint16 m_port;
ConnectionStatus m_status;
QStringList m_assetPlatforms;
bool m_autoConnect;
QThread m_connectionWorkerThread;
QPointer<AssetProcessor::ConnectionWorker> m_connectionWorker;
bool m_runElapsed;
QElapsedTimer m_elapsedTimer;
qint64 m_elapsed;
QString m_elapsedDisplay;
bool m_queuedReconnect = false;
bool m_userCreatedConnection = false;
AZStd::mutex m_responseHandlerMutex;
AZStd::unordered_map<AZ::u32, AssetProcessor::ConnectionBusTraits::ResponseCallback> m_responseHandlerMap;
//metrics
qint64 m_numOpenRequests;
qint64 m_numCloseRequests;
qint64 m_numOpened;
qint64 m_numClosed;
qint64 m_numReadRequests;
qint64 m_numWriteRequests;
qint64 m_numTellRequests;
qint64 m_numSeekRequests;
qint64 m_numEofRequests;
qint64 m_numIsReadOnlyRequests;
qint64 m_numIsDirectoryRequests;
qint64 m_numSizeRequests;
qint64 m_numModificationTimeRequests;
qint64 m_numExistsRequests;
qint64 m_numFlushRequests;
qint64 m_numCreatePathRequests;
qint64 m_numDestroyPathRequests;
qint64 m_numRemoveRequests;
qint64 m_numCopyRequests;
qint64 m_numRenameRequests;
qint64 m_numFindFileNamesRequests;
qint64 m_bytesRead;
qint64 m_bytesWritten;
qint64 m_bytesSent;
qint64 m_bytesReceived;
qint64 m_numOpenFiles;
Q_DISABLE_COPY(Connection)
};
#endif // CONNECTION_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,217 @@
/*
* 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.
*
*/
#ifndef CONNECTIONMANAGER_H
#define CONNECTIONMANAGER_H
#if !defined(Q_MOC_RUN)
#include <AzCore/std/function/function_fwd.h> // <functional> complains about exception handling and is not okay to mix with azcore/etc stuff.
#include <QReadWriteLock>
#include <QMap>
#include <QMultiMap>
#include <QObject>
#include <QString>
#include <QHostAddress>
#include <QStringListModel>
#include "native/utilities/AssetUtilEBusHelper.h"
#include <QAbstractItemModel>
#endif
class Connection;
typedef AZStd::function<void(unsigned int, unsigned int, unsigned int, QByteArray, QString)> regFunc;
typedef QMap<unsigned int, Connection*> ConnectionMap;
typedef QMultiMap<unsigned int, regFunc> RouteMultiMap;
class ConnectionManagerRequests
: public AZ::EBusTraits
{
public:
virtual void RegisterService(unsigned int messageType, regFunc func) = 0;
};
using ConnectionManagerRequestBus = AZ::EBus<ConnectionManagerRequests>;
namespace AssetProcessor
{
class PlatformConfiguration;
}
/** This is a container class for connection
*/
class ConnectionManager
: public QAbstractItemModel,
public ConnectionManagerRequestBus::Handler
{
Q_OBJECT
public:
enum Column
{
StatusColumn,
IdColumn,
IpColumn,
PortColumn,
PlatformColumn,
AutoConnectColumn,
Max
};
enum Roles
{
UserConnectionRole = Qt::UserRole + 1,
};
explicit ConnectionManager(QObject* parent = 0);
virtual ~ConnectionManager();
// Singleton pattern:
static ConnectionManager* Get();
Q_INVOKABLE int getCount() const;
Q_INVOKABLE Connection* getConnection(unsigned int connectionId);
Q_INVOKABLE ConnectionMap& getConnectionMap();
Q_INVOKABLE unsigned int addConnection(qintptr socketDescriptor = -1);
Q_INVOKABLE unsigned int addUserConnection();
Q_INVOKABLE void removeConnection(unsigned int connectionId);
unsigned int GetConnectionId(QString ipaddress, int port);
void SaveConnections(QString settingPrefix = ""); // settingPrefix allowed for testing purposes.
void LoadConnections(QString settingPrefix = ""); // settingPrefix allowed for testing purposes.
void RegisterService(unsigned int type, regFunc func) override;
//QAbstractItemListModel
QVariant data(const QModelIndex& index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
QModelIndex index(int row, int column, const QModelIndex&) const override;
QModelIndex parent(const QModelIndex&) const override;
int columnCount(const QModelIndex& parent) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
Qt::ItemFlags flags(const QModelIndex& index) const override;
bool setData(const QModelIndex& index, const QVariant& value, int role) override;
void removeConnection(const QModelIndex& index);
Q_SIGNALS:
void connectionAdded(unsigned int connectionId, Connection* connection);
void beforeConnectionRemoved(unsigned int connectionId);
void ConnectionDisconnected(unsigned int connectionId);
void ConnectionRemoved(unsigned int connectionId);
void ConnectionError(unsigned int connId, QString error);
void ReadyToQuit(QObject* source);
void SyncWhiteListAndRejectedList(QStringList whiteList, QStringList rejectedList);
// this is a response to the whitelist request with that same token.
void AddressIsWhiteListed(void* token, bool result);
void FirstTimeAddedToRejctedList(QString ipAddress);
public Q_SLOTS:
void SendMessageToService(unsigned int connId, unsigned int type, unsigned int serial, QByteArray payload);
void QuitRequested();
void RemoveConnectionFromMap(unsigned int connectionId);
void MakeSureConnectionMapEmpty();
void NewConnection(qintptr socketDescriptor);
void WhiteListingEnabled(bool enabled);
void IsAddressWhiteListed(QHostAddress hostAddress, void* token);
void AddWhiteListedAddress(QString address);
void RemoveWhiteListedAddress(QString address);
void AddRejectedAddress(QString address, bool surpressWarning = false);
void RemoveRejectedAddress(QString address);
//metrics
void AddBytesReceived(unsigned int connId, qint64 add, bool update);
void AddBytesSent(unsigned int connId, qint64 add, bool update);
void AddBytesRead(unsigned int connId, qint64 add, bool update);
void AddBytesWritten(unsigned int connId, qint64 add, bool update);
void AddOpenRequest(unsigned int connId, bool update);
void AddCloseRequest(unsigned int connId, bool update);
void AddOpened(unsigned int connId, bool update);
void AddClosed(unsigned int connId, bool update);
void AddReadRequest(unsigned int connId, bool update);
void AddWriteRequest(unsigned int connId, bool update);
void AddTellRequest(unsigned int connId, bool update);
void AddSeekRequest(unsigned int connId, bool update);
void AddIsReadOnlyRequest(unsigned int connId, bool update);
void AddIsDirectoryRequest(unsigned int connId, bool update);
void AddSizeRequest(unsigned int connId, bool update);
void AddModificationTimeRequest(unsigned int connId, bool update);
void AddExistsRequest(unsigned int connId, bool update);
void AddFlushRequest(unsigned int connId, bool update);
void AddCreatePathRequest(unsigned int connId, bool update);
void AddDestroyPathRequest(unsigned int connId, bool update);
void AddRemoveRequest(unsigned int connId, bool update);
void AddCopyRequest(unsigned int connId, bool update);
void AddRenameRequest(unsigned int connId, bool update);
void AddFindFileNamesRequest(unsigned int connId, bool update);
void UpdateBytesReceived(unsigned int connId);
void UpdateBytesSent(unsigned int connId);
void UpdateBytesRead(unsigned int connId);
void UpdateBytesWritten(unsigned int connId);
void UpdateOpenRequest(unsigned int connId);
void UpdateCloseRequest(unsigned int connId);
void UpdateOpened(unsigned int connId);
void UpdateClosed(unsigned int connId);
void UpdateReadRequest(unsigned int connId);
void UpdateWriteRequest(unsigned int connId);
void UpdateTellRequest(unsigned int connId);
void UpdateSeekRequest(unsigned int connId);
void UpdateIsReadOnlyRequest(unsigned int connId);
void UpdateIsDirectoryRequest(unsigned int connId);
void UpdateSizeRequest(unsigned int connId);
void UpdateModificationTimeRequest(unsigned int connId);
void UpdateExistsRequest(unsigned int connId);
void UpdateFlushRequest(unsigned int connId);
void UpdateCreatePathRequest(unsigned int connId);
void UpdateDestroyPathRequest(unsigned int connId);
void UpdateRemoveRequest(unsigned int connId);
void UpdateCopyRequest(unsigned int connId);
void UpdateRenameRequest(unsigned int connId);
void UpdateFindFileNamesRequest(unsigned int connId);
void UpdateConnectionMetrics();
void OnStatusChanged(unsigned int connId);
void UpdateWhiteListFromBootStrap();
private:
unsigned int internalAddConnection(bool isUserConnection, qintptr socketDescriptor = -1);
bool IsResponse(unsigned int serial);
void RouteIncomingMessage(unsigned int connId, unsigned int type, unsigned int serial, QByteArray payload);
Connection* FindConnection(const QModelIndex& index) const;
unsigned int m_nextConnectionId;
ConnectionMap m_connectionMap;
RouteMultiMap m_messageRoute;
QHostAddress m_lastHostAddress = QHostAddress::Null;
AZ::u64 m_lastConnectionTimeInUTCMilliSecs = 0;
// keeps track of how many platforms are connected of a given type
// the key is the name of the platform, and the value is the number of those kind of platforms.
QHash<QString, int> m_platformsConnected;
//white listing
bool m_whiteListingEnabled = true;
//these lists are just caches, only used for updating
QStringList m_whiteListedAddresses;
QStringList m_rejectedAddresses;
};
#endif // CONNECTIONMANAGER_H
@@ -0,0 +1,37 @@
/*
* 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.
*
*/
#ifndef ASSETBUILDER_CONNECTIONMESSAGE_H
#define ASSETBUILDER_CONNECTIONMESSAGE_H
#include <QByteArray>
namespace AssetProcessor
{
struct MessageHeader
{
unsigned int type;
unsigned int size;
unsigned int serial;
};
// This is the framing for all packets sent to/from the AssetProcessor
struct Message
{
MessageHeader header;
QByteArray payload;
Message() = default;
Message(const Message&) = default;
};
}
#endif // ASSETBUILDER_CONNECTIONMESSAGE_H
@@ -0,0 +1,486 @@
/*
* 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 "connectionworker.h"
#include "native/utilities/assetUtils.h"
#include <native/utilities/ByteArrayStream.h>
#include <QThread>
#include <QTimer>
#include <QCoreApplication>
#include <QThread>
#include <AzFramework/API/ApplicationAPI.h>
// enable this to debug negotiation - it enables a huge delay so that when a debugger attaches we don't fail.
//#define DEBUG_NEGOTIATION
#undef SendMessage
namespace AssetProcessor {
ConnectionWorker::ConnectionWorker(qintptr /*socketDescriptor*/, QObject* parent)
: QObject(parent)
, m_terminate(false)
{
#ifdef DEBUG_NEGOTIATION
m_waitDelay = 60 * 10 * 1000; // 10 min in debug, in ms
#endif
connect(&m_engineSocket, &QTcpSocket::stateChanged, this, &ConnectionWorker::EngineSocketStateChanged, Qt::QueuedConnection);
#if defined(DEBUG_NEGOTIATION)
AZ_TracePrintf(AssetProcessor::DebugChannel, "Connection::ConnectionWorker created for socket %p: %p", socketDescriptor, this);
#endif
}
ConnectionWorker::~ConnectionWorker()
{
#if defined(DEBUG_NEGOTIATION)
AZ_TracePrintf(AssetProcessor::DebugChannel, "ConnectionWorker::~: %p", this);
#endif
thread()->quit();
}
bool ConnectionWorker::ReadMessage(QTcpSocket& socket, AssetProcessor::Message& message)
{
const qint64 sizeOfHeader = static_cast<qint64>(sizeof(AssetProcessor::MessageHeader));
qint64 bytesAvailable = socket.bytesAvailable();
if (bytesAvailable == 0 || bytesAvailable < sizeOfHeader)
{
return false;
}
// read header
if (!ReadData(socket, (char*)&message.header, sizeOfHeader))
{
DisconnectSockets();
return false;
}
// Prepare the payload buffer
message.payload.resize(message.header.size);
// read payload
if (!ReadData(socket, message.payload.data(), message.header.size))
{
DisconnectSockets();
return false;
}
return true;
}
bool ConnectionWorker::ReadData(QTcpSocket& socket, char* buffer, qint64 size)
{
qint64 bytesRemaining = size;
while (bytesRemaining > 0)
{
// check first, or Qt will throw a warning if we try to do this on an already-disconnected-socket
if (socket.state() != QAbstractSocket::ConnectedState)
{
return false;
}
qint64 bytesRead = socket.read(buffer, bytesRemaining);
if (bytesRead == -1)
{
return false;
}
buffer += bytesRead;
bytesRemaining -= bytesRead;
if (bytesRemaining > 0)
{
socket.waitForReadyRead();
}
}
return true;
}
bool ConnectionWorker::WriteMessage(QTcpSocket& socket, const AssetProcessor::Message& message)
{
const qint64 sizeOfHeader = static_cast<qint64>(sizeof(AssetProcessor::MessageHeader));
AZ_Assert(message.header.size == aznumeric_cast<decltype(message.header.size)>(message.payload.size()), "Message header size does not match payload size");
// Write header
if (!WriteData(socket, (char*)&message.header, sizeOfHeader))
{
DisconnectSockets();
return false;
}
// write payload
if (!WriteData(socket, message.payload.data(), message.payload.size()))
{
DisconnectSockets();
return false;
}
return true;
}
bool ConnectionWorker::WriteData(QTcpSocket& socket, const char* buffer, qint64 size)
{
qint64 bytesRemaining = size;
while (bytesRemaining > 0)
{
// check first, or Qt will throw a warning if we try to do this on an already-disconnected-socket
if (socket.state() != QAbstractSocket::ConnectedState)
{
return false;
}
qint64 bytesWritten = socket.write(buffer, bytesRemaining);
if (bytesWritten == -1)
{
return false;
}
buffer += bytesWritten;
bytesRemaining -= bytesWritten;
}
return true;
}
void ConnectionWorker::EngineSocketHasData()
{
if (m_terminate)
{
return;
}
while (m_engineSocket.bytesAvailable() > 0)
{
AssetProcessor::Message message;
if (ReadMessage(m_engineSocket, message))
{
Q_EMIT ReceiveMessage(message.header.type, message.header.serial, message.payload);
}
else
{
break;
}
}
}
void ConnectionWorker::SendMessage(unsigned int type, unsigned int serial, QByteArray payload)
{
AssetProcessor::Message message;
message.header.type = type;
message.header.serial = serial;
message.header.size = payload.size();
message.payload = payload;
WriteMessage(m_engineSocket, message);
}
namespace Detail
{
template <class N>
bool WriteNegotiation(ConnectionWorker* worker, QTcpSocket& socket, const N& negotiation, unsigned int serial = AzFramework::AssetSystem::NEGOTIATION_SERIAL)
{
AssetProcessor::Message message;
bool packed = AssetProcessor::PackMessage(negotiation, message.payload);
if (packed)
{
message.header.type = negotiation.GetMessageType();
message.header.serial = serial;
message.header.size = message.payload.size();
return worker->WriteMessage(socket, message);
}
return false;
}
template <class N>
bool ReadNegotiation(ConnectionWorker* worker, int waitDelay, QTcpSocket& socket, N& negotiation, unsigned int* serial = nullptr)
{
if (socket.bytesAvailable() == 0)
{
socket.waitForReadyRead(waitDelay);
}
AssetProcessor::Message message;
if (!worker->ReadMessage(socket, message))
{
return false;
}
if (serial)
{
*serial = message.header.serial;
}
return AssetProcessor::UnpackMessage(message.payload, negotiation);
}
}
// Negotiation directly with a game or downstream AssetProcessor:
// if the connection is initiated from this end:
// 1) Send AP Info to downstream engine
// 2) Get downstream engine info
// if there is an incoming connection
// 1) Get downstream engine info
// 2) Send AP Info
bool ConnectionWorker::NegotiateDirect(bool initiate)
{
#if defined(DEBUG_NEGOTIATION)
AZ_TracePrintf(AssetProcessor::DebugChannel, "ConnectionWorker::NegotiateDirect: %p", this);
#endif
using Detail::ReadNegotiation;
using Detail::WriteNegotiation;
using namespace AzFramework::AssetSystem;
AZStd::string azBranchToken;
AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::CalculateBranchTokenForAppRoot, azBranchToken);
QString branchToken(azBranchToken.c_str());
QString projectName = AssetUtilities::ComputeGameName();
NegotiationMessage myInfo;
char processId[20];
azsnprintf(processId, 20, "%lld", QCoreApplication::applicationPid());
myInfo.m_identifier = "ASSETPROCESSOR";
myInfo.m_negotiationInfoMap.insert(AZStd::make_pair(NegotiationInfo_ProcessId, AZ::OSString(processId)));
myInfo.m_negotiationInfoMap.insert(AZStd::make_pair(NegotiationInfo_BranchIndentifier, AZ::OSString(azBranchToken.c_str())));
myInfo.m_negotiationInfoMap.insert(AZStd::make_pair(NegotiationInfo_ProjectName, AZ::OSString(projectName.toUtf8().constData())));
NegotiationMessage engineInfo;
if (initiate)
{
if (!WriteNegotiation(this, m_engineSocket, myInfo))
{
Q_EMIT ErrorMessage("Unable to send negotiation message");
QTimer::singleShot(0, this, SLOT(DisconnectSockets()));
return false;
}
if (!ReadNegotiation(this, m_waitDelay, m_engineSocket, engineInfo))
{
Q_EMIT ErrorMessage("Unable to read negotiation message");
QTimer::singleShot(0, this, SLOT(DisconnectSockets()));
return false;
}
}
else
{
unsigned int serial = 0;
#if defined(DEBUG_NEGOTIATION)
AZ_TracePrintf(AssetProcessor::DebugChannel, "ConnectionWorker::NegotiateDirect: Reading negotiation from engine socket %p", this);
#endif
if (!ReadNegotiation(this, m_waitDelay, m_engineSocket, engineInfo, &serial))
{
#if defined(DEBUG_NEGOTIATION)
AZ_TracePrintf(AssetProcessor::DebugChannel, "ConnectionWorker::NegotiateDirect: no negotation arrived %p", this);
#endif
Q_EMIT ErrorMessage("Unable to read engine negotiation message");
QTimer::singleShot(0, this, SLOT(DisconnectSockets()));
return false;
}
#if defined(DEBUG_NEGOTIATION)
AZ_TracePrintf(AssetProcessor::DebugChannel, "ConnectionWorker::NegotiateDirect: writing negotiation to engine socket %p", this);
#endif
if (!WriteNegotiation(this, m_engineSocket, myInfo, serial))
{
#if defined(DEBUG_NEGOTIATION)
AZ_TracePrintf(AssetProcessor::DebugChannel, "ConnectionWorker::NegotiateDirect: no negotation sent %p", this);
#endif
Q_EMIT ErrorMessage("Unable to send negotiation message");
QTimer::singleShot(0, this, SLOT(DisconnectSockets()));
return false;
}
}
// Skip the process Id validation during negotiation if the identifier is UNITTEST
if (engineInfo.m_identifier != "UNITTEST")
{
if (strncmp(engineInfo.m_negotiationInfoMap[NegotiationInfo_ProcessId].c_str(), processId, strlen(processId)) == 0)
{
Q_EMIT ErrorMessage("Attempted to negotiate with self");
QTimer::singleShot(0, this, SLOT(DisconnectSockets()));
return false;
}
}
if (engineInfo.m_apiVersion != myInfo.m_apiVersion)
{
Q_EMIT ErrorMessage("Negotiation Failed.Version Mismatch.");
QTimer::singleShot(0, this, SLOT(DisconnectSockets()));
return false;
}
QString incomingBranchToken(engineInfo.m_negotiationInfoMap[NegotiationInfo_BranchIndentifier].c_str());
if (QString::compare(incomingBranchToken, branchToken, Qt::CaseInsensitive) != 0)
{
//if we are here it means that the editor/game which is negotiating is running on a different branch
// note that it could have just read nothing from the engine or a repeat packet, in that case, discard it silently and try again.
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "ConnectionWorker::NegotiateDirect: branch token mismatch from %s - %p - %s vs %s\n", engineInfo.m_identifier.c_str(), this, incomingBranchToken.toUtf8().data(), branchToken.toUtf8().data());
AssetProcessor::MessageInfoBus::Broadcast(&AssetProcessor::MessageInfoBus::Events::NegotiationFailed);
QTimer::singleShot(0, this, SLOT(DisconnectSockets()));
return false;
}
QString incomingProjectName(engineInfo.m_negotiationInfoMap[NegotiationInfo_ProjectName].c_str());
// Do a case-insensitive compare for the project name because some (case-sensitive) platforms will blower-case the incoming project name
if(QString::compare(incomingProjectName, projectName, Qt::CaseInsensitive) != 0)
{
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "ConnectionWorker::NegotiateDirect: project name mismatch from %s - %p - %s vs %s\n", engineInfo.m_identifier.c_str(), this, incomingProjectName.toUtf8().constData(), projectName.toUtf8().constData());
AssetProcessor::MessageInfoBus::Broadcast(&AssetProcessor::MessageInfoBus::Events::NegotiationFailed);
QTimer::singleShot(0, this, SLOT(DisconnectSockets()));
return false;
}
Q_EMIT Identifier(engineInfo.m_identifier.c_str());
Q_EMIT AssetPlatformsString(engineInfo.m_negotiationInfoMap[NegotiationInfo_Platform].c_str());
#if defined(DEBUG_NEGOTIATION)
AZ_TracePrintf(AssetProcessor::DebugChannel, "ConnectionWorker::NegotiateDirect: negotation complete %p", this);
#endif
Q_EMIT ConnectionEstablished(m_engineSocket.peerAddress().toString(), m_engineSocket.peerPort());
connect(&m_engineSocket, &QTcpSocket::readyRead, this, &ConnectionWorker::EngineSocketHasData);
// force the socket to evaluate any data recv'd between negotiation and now
QTimer::singleShot(0, this, SLOT(EngineSocketHasData()));
return true;
}
// RequestTerminate can be called from anywhere, so we queue the actual
// termination to ensure it happens in the worker's thread
void ConnectionWorker::RequestTerminate()
{
if (!m_alreadySentTermination)
{
m_terminate = true;
m_alreadySentTermination = true;
QMetaObject::invokeMethod(this, "TerminateConnection", Qt::BlockingQueuedConnection);
}
}
void ConnectionWorker::TerminateConnection()
{
disconnect(&m_engineSocket, &QTcpSocket::stateChanged, this, &ConnectionWorker::EngineSocketStateChanged);
DisconnectSockets();
deleteLater();
}
void ConnectionWorker::ConnectSocket(qintptr socketDescriptor)
{
AZ_Assert(socketDescriptor != -1, "ConectionWorker::ConnectSocket: Supplied socket is invalid");
if (socketDescriptor != -1)
{
// calling setSocketDescriptor will cause it to invoke EngineSocketStateChanged instantly, which we don't want, so disconnect it temporarily.
disconnect(&m_engineSocket, &QTcpSocket::stateChanged, this, &ConnectionWorker::EngineSocketStateChanged);
m_engineSocket.setSocketDescriptor(socketDescriptor, QAbstractSocket::ConnectedState, QIODevice::ReadWrite);
Q_EMIT IsAddressWhiteListed(m_engineSocket.peerAddress(), reinterpret_cast<void*>(this));
}
}
void ConnectionWorker::AddressIsWhiteListed(void* token, bool result)
{
if (reinterpret_cast<void*>(this) == token)
{
if (result)
{
// this address has been approved, connect and proceed
connect(&m_engineSocket, &QTcpSocket::stateChanged, this, &ConnectionWorker::EngineSocketStateChanged);
EngineSocketStateChanged(QAbstractSocket::ConnectedState);
}
else
{
// this address has been rejected, disconnect immediately!!!
AZ_TracePrintf(AssetProcessor::ConsoleChannel, " A connection attempt was ignored because it is not whitelisted. Please consider adding white_list=(IP ADDRESS),localhost to the bootstrap.cfg");
disconnect(&m_engineSocket, &QTcpSocket::readyRead, this, &ConnectionWorker::EngineSocketHasData);
DisconnectSockets();
}
}
}
void ConnectionWorker::ConnectToEngine(QString ipAddress, quint16 port)
{
#if defined(DEBUG_NEGOTIATION)
AZ_TracePrintf(AssetProcessor::DebugChannel, " ConnectionWorker::ConnectToEngine");
#endif
m_terminate = false;
if (m_engineSocket.state() == QAbstractSocket::UnconnectedState)
{
m_initiatedConnection = true;
m_engineSocket.connectToHost(ipAddress, port, QIODevice::ReadWrite);
}
}
void ConnectionWorker::EngineSocketStateChanged(QAbstractSocket::SocketState socketState)
{
#if defined(DEBUG_NEGOTIATION)
AZ_TracePrintf(AssetProcessor::DebugChannel, " ConnectionWorker::EngineSocketStateChanged to %i", (int)socketState);
#endif
if (m_terminate)
{
return;
}
if (socketState == QAbstractSocket::ConnectedState)
{
m_engineSocket.setSocketOption(QAbstractSocket::KeepAliveOption, 1);
m_engineSocket.setSocketOption(QAbstractSocket::LowDelayOption, 1); //disable nagles algorithm
m_engineSocketIsConnected = true;
#if defined(DEBUG_NEGOTIATION)
AZ_TracePrintf(AssetProcessor::DebugChannel, "ConnectionWorker::EngineSocketStateChanged: %p connected now (%s)", this, m_engineSocketIsConnected ? "True" : "False");
#endif
QMetaObject::invokeMethod(this, "NegotiateDirect", Qt::QueuedConnection, Q_ARG(bool, m_initiatedConnection));
}
else if (socketState == QAbstractSocket::UnconnectedState)
{
m_engineSocketIsConnected = false;
#if defined(DEBUG_NEGOTIATION)
AZ_TracePrintf(AssetProcessor::DebugChannel, "ConnectionWorker::EngineSocketStateChanged: %p unconnected, now (%s)", this, m_engineSocketIsConnected ? "True" : "False");
#endif
disconnect(&m_engineSocket, &QTcpSocket::readyRead, 0, 0);
DisconnectSockets();
}
}
void ConnectionWorker::DisconnectSockets()
{
#if defined(DEBUG_NEGOTIATION)
AZ_TracePrintf(AssetProcessor::DebugChannel, " ConnectionWorker::DisconnectSockets");
#endif
m_engineSocket.abort();
m_engineSocket.close();
Q_EMIT ConnectionDisconnected();
}
void ConnectionWorker::Reset()
{
m_terminate = false;
}
bool ConnectionWorker::Terminate()
{
return m_terminate;
}
QTcpSocket& ConnectionWorker::GetSocket()
{
return m_engineSocket;
}
bool ConnectionWorker::InitiatedConnection() const
{
return m_initiatedConnection;
}
} // namespace AssetProcessor
@@ -0,0 +1,88 @@
/*
* 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.
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <QTcpSocket>
#include <QHostAddress>
#include "native/connection/connectionMessages.h"
#include <AzCore/Memory/Memory.h>
#include <AzCore/Memory/SystemAllocator.h>
#endif
/** This Class is responsible for connecting to the client
*/
#undef SendMessage
namespace AssetProcessor
{
class ConnectionWorker
: public QObject
{
Q_OBJECT
public:
explicit ConnectionWorker(qintptr socketDescriptor = -1, QObject* parent = 0);
virtual ~ConnectionWorker();
QTcpSocket& GetSocket();
void Reset();
bool Terminate();
bool ReadMessage(QTcpSocket& socket, AssetProcessor::Message& message);
bool ReadData(QTcpSocket& socket, char* buffer, qint64 size);
bool WriteMessage(QTcpSocket& socket, const AssetProcessor::Message& message);
bool WriteData(QTcpSocket& socket, const char* buffer, qint64 size);
//! True if we initiated the connection, false if someone connected to us.
bool InitiatedConnection() const;
Q_SIGNALS:
void ReceiveMessage(unsigned int type, unsigned int serial, QByteArray payload);
void SocketIPAddress(QString ipAddress);
void SocketPort(int port);
void Identifier(QString identifier);
void AssetPlatformsString(QString platform);
void ConnectionDisconnected();
void ConnectionEstablished(QString ipAddress, quint16 port);
void ErrorMessage(QString msg);
// the token identifies the unique connection instance, since multiple may have the same address
void IsAddressWhiteListed(QHostAddress hostAddress, void* token);
public Q_SLOTS:
void ConnectSocket(qintptr socketDescriptor);
void ConnectToEngine(QString ipAddress, quint16 port);
void EngineSocketHasData();
void EngineSocketStateChanged(QAbstractSocket::SocketState socketState);
void SendMessage(unsigned int type, unsigned int serial, QByteArray payload);
void DisconnectSockets();
void RequestTerminate();
bool NegotiateDirect(bool initiate);
// the token will be the same token sent in the whitelisting request.
void AddressIsWhiteListed(void* token, bool result);
private Q_SLOTS:
void TerminateConnection();
private:
QTcpSocket m_engineSocket;
volatile bool m_terminate;
volatile bool m_alreadySentTermination = false;
bool m_initiatedConnection = false;
bool m_engineSocketIsConnected = false;
int m_waitDelay = 10000; //increased to 10000 as 5000 milliseconds was enough in the unloaded general case but when the computer is loaded we need more time to negotiate a connection or we only get connection failures
};
} // namespace AssetProcessor