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/CryEngine/CrySystem/RemoteCommand.h

460 lines
14 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Remote command system implementation
#pragma once
//-----------------------------------------------------------------------------
#include "IServiceNetwork.h"
#include "IRemoteCommand.h"
#include "CryThread.h"
class CRemoteCommandManager;
// Remote command client implementation
class CRemoteCommandClient
: public IRemoteCommandClient
, public CryRunnable
{
protected:
//-------------------------------------------------------------
class Command
{
public:
ILINE IServiceNetworkMessage* GetMessage() const
{
return m_pMessage;
}
ILINE uint32 GetCommandId() const
{
return m_id;
}
public:
// Create command data from serializing a remote command object
static Command* Compile(const IRemoteCommand& cmd, const uint32 commandId, const uint32 classId);
void AddRef();
void Release();
private:
Command();
~Command();
volatile int m_refCount;
uint32 m_id;
const char* m_szClassName; // debug only
IServiceNetworkMessage* m_pMessage;
};
//-------------------------------------------------------------
// Local connection reference to command
// NOTE: pCommand is reference counted from the calling code
struct CommandRef
{
Command* m_pCommand;
uint64 m_lastSentTime;
ILINE CommandRef()
: m_pCommand(NULL)
, m_lastSentTime(0)
{}
ILINE CommandRef(Command* pCommand)
: m_pCommand(pCommand)
, m_lastSentTime(0)
{}
// Order function for set container (we want to keep the commands sorted by ID)
static ILINE bool CompareCommandRefs(CommandRef* const& a, CommandRef* const& b)
{
return a->m_pCommand->GetCommandId() < b->m_pCommand->GetCommandId();
}
};
//-------------------------------------------------------------
// Remote server connection wrapper
class Connection
: public IRemoteCommandConnection
{
// How many commands we can send upfront before waiting for an ACK
static const uint32 kCommandSendLead = 50;
// How much command data can be merged into a single packet (KB)
static const uint32 kCommandMaxMergePacketSize = 1024;
// Time after which we start resending commands (ms)
static const uint32 kCommandResendTime = 2000;
protected:
CRemoteCommandManager* m_pManager;
volatile int m_refCount;
// Connection (from service network layer)
IServiceNetworkConnection* m_pConnection;
// Cached address of the remote endpoint
ServiceNetworkAddress m_remoteAddress;
// Pending commands, they are kept ed here until they are ACKed as executed by server
typedef std::vector<CommandRef*> TCommands;
TCommands m_pCommands;
CryMutex m_commandAccessMutex;
// A queue of raw messages
typedef CryMT::CLocklessPointerQueue<IServiceNetworkMessage> TRawMessageQueue;
TRawMessageQueue m_pRawMessages;
CryMutex m_rawMessagesMutex;
// Last command that was ACKed as received by server
// This is used to synchronize the both ends of the pipeline
uint32 m_lastReceivedCommand;
// Last command that was ACKed as executed by server
// This is used to synchronize the both ends of the pipeline
uint32 m_lastExecutedCommand;
public:
ILINE CRemoteCommandManager* GetManager() const
{
return m_pManager;
}
public:
Connection(CRemoteCommandManager* pManager, IServiceNetworkConnection* pConnection, uint32 currentCommandId);
// Add command to sending queue in this connection
void AddToSendQueue(Command* pCommand);
// Process the communication, returns false if connection should be deleted
bool Update();
// Send the "disconnect" message to the remote side therefore gracefully closing the connection.
void SendDisconnectMessage();
public:
// IRemoteCommandConnection interface implementation
virtual bool IsAlive() const;
virtual const ServiceNetworkAddress& GetRemoteAddress() const;
virtual bool SendRawMessage(IServiceNetworkMessage* pMessage);
virtual IServiceNetworkMessage* ReceiveRawMessage();
virtual void Close(bool bFlushQueueBeforeClosing = false);
virtual void AddRef();
virtual void Release();
private:
~Connection();
};
protected:
CRemoteCommandManager* m_pManager;
typedef std::vector<Connection*> TConnections;
TConnections m_pConnections;
TConnections m_pConnectionsToDelete;
CryMutex m_accessMutex;
// Local command ID counter, incremented atomically using CryInterlockedIncrement
volatile uint32 m_commandId;
typedef CryThread<CRemoteCommandClient> TRemoteClientThread;
TRemoteClientThread* m_pThread;
CryEvent m_threadEvent;
bool m_bCloseThread;
public:
ILINE CRemoteCommandManager* GetManager() const
{
return m_pManager;
}
public:
CRemoteCommandClient(CRemoteCommandManager* pManager);
virtual ~CRemoteCommandClient();
// IRemoteCommandClient interface
virtual void Delete();
virtual bool Schedule(const IRemoteCommand& command);
virtual IRemoteCommandConnection* ConnectToServer(const class ServiceNetworkAddress& serverAddress);
// CryRunnable interface implementation
virtual void Run();
virtual void Cancel();
};
//-----------------------------------------------------------------------------
// Remote command server implementation
class CRemoteCommandServer
: public IRemoteCommandServer
, public CryRunnable
{
#if defined(AZ_RESTRICTED_PLATFORM)
#include AZ_RESTRICTED_FILE(RemoteCommand_h)
#endif
protected:
// Wrapped commands
class WrappedCommand
{
private:
IRemoteCommand* m_pCommand;
volatile int m_refCount;
uint32 m_commandID;
public:
ILINE const uint32 GetId() const
{
return m_commandID;
}
ILINE IRemoteCommand* GetCommand() const
{
return m_pCommand;
}
public:
WrappedCommand(IRemoteCommand* pCommand, const uint32 commandId);
void AddRef();
void Release();
private:
~WrappedCommand();
};
// Local endpoint
class Endpoint
{
private:
IServiceNetworkConnection* m_pConnection;
class CRemoteCommandServer* m_pServer;
CRemoteCommandManager* m_pManager;
// ACK counters for synchronization
uint32 m_lastReceivedCommand;
uint32 m_lastExecutedCommand;
uint32 m_lastReceivedCommandACKed;
uint32 m_lastExecutedCommandACKed;
CryMutex m_accessLock;
// We have received class list (it's a valid RC connection)
bool m_bHasReceivedClassList;
// Locally mapped class id (because IDs on remote side can be different than here)
typedef std::vector< IRemoteCommandClass* > TLocalClassFactoryList;
TLocalClassFactoryList m_pLocalClassFactories;
// Commands that were received and should be executed
typedef CryMT::CLocklessPointerQueue< WrappedCommand > TCommandQueue;
TCommandQueue m_pCommandsToExecute;
CryMutex m_commandListLock;
public:
ILINE CRemoteCommandManager* GetManager() const
{
return m_pManager;
}
// Get the endpoint connection
ILINE IServiceNetworkConnection* GetConnection() const
{
return m_pConnection;
}
// Have we received a class list from the client
ILINE bool HasReceivedClassList() const
{
return m_bHasReceivedClassList;
}
public:
Endpoint(CRemoteCommandManager* pManager, class CRemoteCommandServer* pServer, IServiceNetworkConnection* pConnection);
~Endpoint();
// Execute pending commands (called from main thread)
void Execute();
// Update (send/receive, etc) Returns false if endpoint died.
bool Update();
// Get the class name as translated by this endpoint (by ID)
const char* GetClassName(const uint32 classId) const;
// Create command object by class ID
IRemoteCommand* CreateObject(const uint32 classId) const;
};
// Received raw message
// Beware to use always via pointer to this type since propper reference counting is not implemented for copy and assigment
struct RawMessage
{
// We keep a reference to connection so we know where to send the response
IServiceNetworkConnection* m_pConnection;
IServiceNetworkMessage* m_pMessage;
ILINE RawMessage(IServiceNetworkConnection* pConnection, IServiceNetworkMessage* pMessage)
: m_pConnection(pConnection)
, m_pMessage(pMessage)
{
m_pMessage->AddRef();
m_pConnection->AddRef();
}
ILINE ~RawMessage()
{
m_pMessage->Release();
m_pConnection->Release();
}
private:
ILINE RawMessage([[maybe_unused]] const RawMessage& other) {};
ILINE RawMessage& operator==([[maybe_unused]] const RawMessage& other) { return *this; }
};
protected:
CRemoteCommandManager* m_pManager;
// Network listening socket
IServiceNetworkListener* m_pListener;
// Live endpoints
typedef std::vector<Endpoint*> TEndpoints;
TEndpoints m_pEndpoints;
TEndpoints m_pUpdateEndpoints;
CryMutex m_accessLock;
// Endpoints that were discarded and should be deleted
// We can delete endpoints only from the update thread
TEndpoints m_pEndpointToDelete;
// Received raw messages
typedef CryMT::CLocklessPointerQueue<RawMessage> TRawMessagesQueue;
TRawMessagesQueue m_pRawMessages;
CryMutex m_rawMessagesLock;
// Listeners for raw messages that require synchronous processing
typedef std::vector<IRemoteCommandListenerSync*> TRawMessageListenersSync;
TRawMessageListenersSync m_pRawListenersSync;
// Listeners for raw messages that can be processed asynchronously (faster path)
typedef std::vector<IRemoteCommandListenerAsync*> TRawMessageListenersAsync;
TRawMessageListenersAsync m_pRawListenersAsync;
// Command communication and deserialization is done on thread
typedef CryThread<CRemoteCommandServer> TRemoteServerThread;
TRemoteServerThread* m_pThread;
// Suppression counter (execution of commands is suppressed when>0)
// This is updated using CryInterlocked* functions
volatile int m_suppressionCounter;
bool m_bIsSuppressed;
// Request to close the network thread
bool m_bCloseThread;
public:
ILINE CRemoteCommandManager* GetManager() const
{
return m_pManager;
}
public:
CRemoteCommandServer(CRemoteCommandManager* pManager, IServiceNetworkListener* pListener);
virtual ~CRemoteCommandServer();
// IRemoteCommandServer interface implementation
virtual void Delete();
virtual void FlushCommandQueue();
virtual void SuppressCommands();
virtual void ResumeCommands();
virtual void RegisterSyncMessageListener(IRemoteCommandListenerSync* pListener);
virtual void UnregisterSyncMessageListener(IRemoteCommandListenerSync* pListener);
virtual void RegisterAsyncMessageListener(IRemoteCommandListenerAsync* pListener);
virtual void UnregisterAsyncMessageListener(IRemoteCommandListenerAsync* pListener);
virtual void Broadcast(IServiceNetworkMessage* pMessage);
virtual bool HasConnectedClients() const;
// CryRunnable
virtual void Run();
virtual void Cancel();
protected:
void ProcessRawMessageAsync(IServiceNetworkMessage* pMessage, IServiceNetworkConnection* pConnection);
void ProcessRawMessagesSync();
};
//-----------------------------------------------------------------------------
// Remote command manager implementation
class CRemoteCommandManager
: public IRemoteCommandManager
{
public:
CRemoteCommandManager();
virtual ~CRemoteCommandManager();
// IRemoteCommandManager interface implementation
virtual void SetVerbosityLevel(const uint32 level);
virtual IRemoteCommandServer* CreateServer(uint16 localPort);
virtual IRemoteCommandClient* CreateClient();
virtual void RegisterCommandClass(IRemoteCommandClass& commandClass);
// Debug print
#ifdef RELEASE
void Log([[maybe_unused]] const char* txt, ...) const {};
bool CheckVerbose([[maybe_unused]] const uint32 level) const { return false; }
#else
void Log(const char* txt, ...) const;
bool CheckVerbose(const uint32 level) const;
#endif
// Build ID->Class Factory mapping given the class name list, will report errors to the log.
void BuildClassMapping(const std::vector<string>& classNames, std::vector< IRemoteCommandClass* >& outClasses);
// Get list of class names (in order of their IDs)
void GetClassList(std::vector<string>& outClassNames) const;
// Find class ID for given class, returns false if not found
bool FindClassId(IRemoteCommandClass* commandClass, uint32& outClassId) const;
public:
ILINE CRemoteCommandManager* GetManager()
{
return this;
}
private:
// Class name mapping
typedef std::map< string, IRemoteCommandClass* > TClassMap;
TClassMap m_pClasses;
// Class ID lookup
typedef std::vector< IRemoteCommandClass* > TClassIDList;
TClassIDList m_pClassesByID;
// Class ID mapping
typedef std::map< string, int > TClassIDMap;
TClassIDMap m_pClassesMap;
// Verbose level
ICVar* m_pVerboseLevel;
};