2023-11-29 18:47:11 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "UbaNetworkClient.h"
# include "UbaCrypto.h"
# include "UbaBinaryReaderWriter.h"
# include "UbaNetworkBackendTcp.h"
# include "UbaNetworkMessage.h"
# include <stdlib.h>
# include <stdio.h>
namespace uba
{
NetworkClient : : NetworkClient ( bool & outCtorSuccess , const NetworkClientCreateInfo & info , const tchar * name )
2024-08-28 13:33:28 -04:00
: WorkManagerImpl ( info . workerCount = = 0 ? GetLogicalProcessorCount ( ) : info . workerCount )
2023-11-29 18:47:11 -05:00
, m_logWriter ( info . logWriter )
, m_logger ( info . logWriter , SetGetPrefix ( name ) )
, m_isConnected ( true )
2023-12-14 13:31:47 -05:00
, m_isOrWasConnected ( true )
2023-11-29 18:47:11 -05:00
{
outCtorSuccess = true ;
u32 fixedSendSize = Max ( info . sendSize , ( u32 ) ( 4 * 1024 ) ) ;
fixedSendSize = Min ( fixedSendSize , ( u32 ) ( SendMaxSize ) ) ;
if ( info . sendSize ! = fixedSendSize )
m_logger . Detail ( TC ( " Adjusted msg size to %u to stay inside limits " ) , fixedSendSize ) ;
m_sendSize = fixedSendSize ;
m_receiveTimeoutSeconds = info . receiveTimeoutSeconds ;
m_connectionsIt = m_connections . end ( ) ;
if ( info . cryptoKey128 )
{
m_cryptoKey = Crypto : : CreateKey ( m_logger , info . cryptoKey128 ) ;
if ( m_cryptoKey = = InvalidCryptoKey )
outCtorSuccess = false ;
}
}
NetworkClient : : ~ NetworkClient ( )
{
2024-03-08 18:31:48 -05:00
UBA_ASSERTF ( m_connections . empty ( ) , TC ( " Client still has connections (%llu). %s " ) , m_connections . size ( ) , m_isDisconnecting ? TC ( " " ) : TC ( " Disconnect has not been called " ) ) ;
2023-11-29 18:47:11 -05:00
if ( m_cryptoKey )
Crypto : : DestroyKey ( m_cryptoKey ) ;
}
bool NetworkClient : : Connect ( NetworkBackend & backend , const tchar * ip , u16 port , bool * timedOut )
{
return backend . Connect ( m_logger , ip , [ & ] ( void * connection , const sockaddr & remoteSocketAddr , bool * timedOut )
{
return AddConnection ( backend , connection , timedOut ) ;
} , port , timedOut ) ;
}
bool NetworkClient : : AddConnection ( NetworkBackend & backend , void * backendConnection , bool * timedOut )
{
struct RecvContext
{
2024-02-23 00:59:47 -05:00
RecvContext ( NetworkClient & c , NetworkBackend & b , void * bc ) : client ( c ) , backend ( b ) , backendConnection ( bc ) , recvEvent ( true ) , exitScopeEvent ( true )
{
2024-03-08 18:31:48 -05:00
error = 255 ;
2024-02-23 00:59:47 -05:00
}
~ RecvContext ( )
{
if ( error )
backend . Shutdown ( backendConnection ) ;
exitScopeEvent . IsSet ( ~ 0u ) ;
}
2023-11-29 18:47:11 -05:00
NetworkClient & client ;
NetworkBackend & backend ;
void * backendConnection ;
Event recvEvent ;
2024-02-23 00:59:47 -05:00
Event exitScopeEvent ;
2024-03-08 18:31:48 -05:00
Atomic < u8 > error ;
2024-02-23 00:59:47 -05:00
} ;
RecvContext rc ( * this , backend , backendConnection ) ;
// The only way out of this function is to get a call to one of the below callbacks since exitScopeEvent must be set.
2023-11-29 18:47:11 -05:00
2024-03-03 21:07:10 -05:00
backend . SetDisconnectCallback ( backendConnection , & rc , [ ] ( void * context , const Guid & connectionUid , void * connection )
2023-11-29 18:47:11 -05:00
{
2024-02-23 00:59:47 -05:00
auto & rc = * ( RecvContext * ) context ;
2024-03-08 18:31:48 -05:00
if ( rc . error = = 0 )
rc . error = 4 ;
2024-02-23 00:59:47 -05:00
rc . recvEvent . Set ( ) ;
rc . exitScopeEvent . Set ( ) ;
2023-11-29 18:47:11 -05:00
} ) ;
2024-03-03 21:07:10 -05:00
backend . SetRecvCallbacks ( backendConnection , & rc , 1 + sizeof ( Guid ) , [ ] ( void * context , const Guid & connectionUid , u8 * headerData , void * & outBodyContext , u8 * & outBodyData , u32 & outBodySize )
2023-11-29 18:47:11 -05:00
{
2024-02-23 00:59:47 -05:00
auto & rc = * ( RecvContext * ) context ;
rc . error = * headerData ;
2024-02-14 01:23:19 -05:00
Guid serverUid = * ( Guid * ) ( headerData + 1 ) ;
2024-02-23 00:59:47 -05:00
if ( ! rc . error )
2024-02-14 01:23:19 -05:00
{
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( rc . client . m_serverUidLock , lock ) ;
2024-02-23 00:59:47 -05:00
if ( rc . client . m_serverUid = = Guid ( ) )
rc . client . m_serverUid = serverUid ;
else if ( rc . client . m_serverUid ! = serverUid ) // Seems like two different servers tried to connect to this client.. keep the first one and ignore the others
rc . error = 5 ;
2024-02-14 01:23:19 -05:00
}
2024-02-23 00:59:47 -05:00
if ( ! rc . error )
2024-03-08 18:31:48 -05:00
if ( ! rc . client . ConnectedCallback ( rc . backend , rc . backendConnection ) )
rc . error = 4 ;
if ( rc . error ! = 0 )
return false ;
2024-02-14 01:23:19 -05:00
2024-02-23 00:59:47 -05:00
rc . recvEvent . Set ( ) ;
rc . exitScopeEvent . Set ( ) ;
2023-11-29 18:47:11 -05:00
return true ;
2024-02-23 00:59:47 -05:00
2023-11-29 18:47:11 -05:00
} , nullptr , TC ( " Connecting " ) ) ;
if ( m_cryptoKey )
{
// If we have a crypto key we start by sending a predefined 128 bytes blob that is encrypted.
// If server decrypt it to the same blob, we're good on that part
u8 encryptedBuffer [ 1024 ] ;
memcpy ( encryptedBuffer , EncryptionHandshakeString , sizeof ( EncryptionHandshakeString ) ) ;
if ( ! Crypto : : Encrypt ( m_logger , m_cryptoKey , encryptedBuffer , sizeof ( EncryptionHandshakeString ) ) )
return false ;
NetworkBackend : : SendContext handskakeContext ;
if ( ! backend . Send ( m_logger , backendConnection , encryptedBuffer , sizeof ( EncryptionHandshakeString ) , handskakeContext ) )
return false ;
}
u32 version = SystemNetworkVersion ;
NetworkBackend : : SendContext versionContext ;
if ( ! backend . Send ( m_logger , backendConnection , & version , sizeof ( version ) , versionContext ) )
return false ;
NetworkBackend : : SendContext uidContext ;
if ( ! backend . Send ( m_logger , backendConnection , & m_uid , sizeof ( m_uid ) , uidContext ) )
return false ;
2024-02-23 00:59:47 -05:00
if ( ! rc . recvEvent . IsSet ( ~ 0u ) ) // This can not happen. Since both callbacks are using rc we can't leave this function until we know we are not in the callbacks
2023-11-29 18:47:11 -05:00
return m_logger . Error ( TC ( " Timed out waiting for connection response from server " ) ) ;
2023-12-14 13:31:47 -05:00
m_isOrWasConnected . Set ( ) ;
2023-11-29 18:47:11 -05:00
if ( rc . error = = 1 ) // Bad version
return m_logger . Error ( TC ( " Version mismatch with server " ) ) ;
if ( rc . error = = 2 )
return m_logger . Error ( TC ( " Server failed to receive client uid " ) ) ;
if ( rc . error = = 3 )
{
if ( ! timedOut )
return m_logger . Error ( TC ( " Server does not allow new clients " ) ) ;
* timedOut = true ;
Sleep ( 1000 ) ; // Kind of ugly, but we want the retry-clients to keep retrying so we pretend it is a timeout
return false ;
}
if ( rc . error = = 4 )
{
if ( ! timedOut )
return m_logger . Error ( TC ( " Server disconnected " ) ) ;
* timedOut = true ;
Sleep ( 1000 ) ; // Kind of ugly, but we want the retry-clients to keep retrying so we pretend it is a timeout
return false ;
}
2024-02-14 01:23:19 -05:00
if ( rc . error = = 5 )
{
m_logger . Warning ( TC ( " A connection from a server with different uid was requested. Ignore " ) ) ;
return false ;
}
2023-11-29 18:47:11 -05:00
if ( m_connectionCount . fetch_add ( 1 ) ! = 0 )
return true ;
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_onConnectedFunctionsLock , lock ) ;
2023-11-29 18:47:11 -05:00
for ( auto & f : m_onConnectedFunctions )
f ( ) ;
2024-07-04 10:58:32 -04:00
m_isConnected . Set ( ) ;
2023-11-29 18:47:11 -05:00
lock . Leave ( ) ;
return true ;
}
constexpr u32 SendHeaderSize = 6 ;
constexpr u32 ReceiveHeaderSize = 5 ;
2024-03-08 18:31:48 -05:00
void NetworkClient : : DisconnectCallback ( void * context , const Guid & connectionUid , void * connection )
{
auto & c = * ( Connection * ) context ;
2024-07-16 20:07:03 -04:00
c . owner . OnDisconnected ( c , 1 ) ;
2024-03-08 18:31:48 -05:00
c . disconnectedEvent . Set ( ) ;
}
bool NetworkClient : : ConnectedCallback ( NetworkBackend & backend , void * backendConnection )
2023-11-29 18:47:11 -05:00
{
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_connectionsLock , lock ) ;
2024-03-08 18:31:48 -05:00
if ( m_isDisconnecting )
return false ;
2023-11-29 18:47:11 -05:00
m_connections . emplace_back ( * this ) ;
Connection * connection = & m_connections . back ( ) ;
connection - > backendConnection = backendConnection ;
connection - > connected = 1 ;
connection - > backend = & backend ;
2024-07-16 20:07:03 -04:00
SCOPED_WRITE_LOCK ( m_connectionsItLock , l ) ; // Take this lock to make sure callbacks are set before connection is used
2024-07-16 21:58:09 -04:00
m_connectionsIt = - - m_connections . end ( ) ;
2024-07-16 20:07:03 -04:00
m_logger . Detail ( TC ( " Connected to server... (0x%p) " ) , backendConnection ) ;
2023-11-29 18:47:11 -05:00
lock . Leave ( ) ;
2024-03-08 18:31:48 -05:00
backend . SetDisconnectCallback ( backendConnection , connection , DisconnectCallback ) ;
2023-11-29 18:47:11 -05:00
backend . SetRecvCallbacks ( backendConnection , connection , ReceiveHeaderSize , ReceiveResponseHeader , ReceiveResponseBody , TC ( " ReceiveMessageResponse " ) ) ;
2024-03-08 18:31:48 -05:00
return true ;
2023-11-29 18:47:11 -05:00
}
2024-03-03 21:07:10 -05:00
bool NetworkClient : : ReceiveResponseHeader ( void * context , const Guid & connectionUid , u8 * headerData , void * & outBodyContext , u8 * & outBodyData , u32 & outBodySize )
2023-11-29 18:47:11 -05:00
{
auto & connection = * ( Connection * ) context ;
auto & client = connection . owner ;
u16 messageId = u16 ( headerData [ 0 ] < < 8 ) | u16 ( ( * ( u32 * ) ( headerData + 1 ) & 0xff000000 ) > > 24 ) ;
u32 messageSize = * ( u32 * ) ( headerData + 1 ) & 0x00FFFFFF ;
2024-02-26 01:14:42 -05:00
SCOPED_READ_LOCK ( client . m_activeMessagesLock , lock ) ;
2023-11-29 18:47:11 -05:00
if ( ! connection . connected )
return false ;
UBA_ASSERTF ( messageId < client . m_activeMessages . size ( ) , TC ( " Message id %u is higher than max %u " ) , messageId , u32 ( client . m_activeMessages . size ( ) ) ) ;
NetworkMessage * msg = client . m_activeMessages [ messageId ] ;
lock . Leave ( ) ;
UBA_ASSERT ( msg ) ;
constexpr u32 ErrorSize = 0xffffff - ReceiveHeaderSize ; // ReceiveHeaderSize is removed from size in server send
if ( messageSize = = ErrorSize )
{
2024-06-14 17:41:02 -04:00
msg - > m_error = 1 ;
2024-02-28 00:30:17 -05:00
msg - > Done ( ) ;
2023-11-29 18:47:11 -05:00
return true ;
}
else if ( ! messageSize )
{
2024-03-03 21:07:10 -05:00
+ + client . m_recvCount ;
2024-02-28 00:30:17 -05:00
msg - > Done ( ) ;
2023-11-29 18:47:11 -05:00
return true ;
}
UBA_ASSERTF ( messageSize < = msg - > m_responseCapacity , TC ( " Message size is %u but reader capacity is only %u " ) , messageSize , msg - > m_responseCapacity ) ;
msg - > m_responseSize = messageSize ;
outBodyContext = msg ;
outBodyData = ( u8 * ) msg - > m_response ;
outBodySize = messageSize ;
2024-03-03 21:07:10 -05:00
+ + client . m_recvCount ;
client . m_recvBytes + = ReceiveHeaderSize + messageSize ;
2023-11-29 18:47:11 -05:00
return true ;
}
bool NetworkClient : : ReceiveResponseBody ( void * context , bool recvError , u8 * headerData , void * bodyContext , u8 * bodyData , u32 bodySize )
{
auto & msg = * ( NetworkMessage * ) bodyContext ;
if ( recvError )
2024-06-14 17:41:02 -04:00
msg . m_error = 2 ;
2024-02-28 00:30:17 -05:00
msg . Done ( ) ;
2023-11-29 18:47:11 -05:00
return true ;
}
void NetworkClient : : Disconnect ( )
{
2024-03-08 18:31:48 -05:00
m_isDisconnecting = true ;
2024-02-22 01:56:35 -05:00
{
2024-03-08 18:31:48 -05:00
SCOPED_READ_LOCK ( m_connectionsLock , lock ) ;
for ( auto & c : m_connections )
{
2024-07-16 20:07:03 -04:00
OnDisconnected ( c , 0 ) ;
2024-03-08 18:31:48 -05:00
c . disconnectedEvent . IsSet ( ~ 0u ) ;
}
2024-02-22 01:56:35 -05:00
}
2024-03-08 18:31:48 -05:00
{
SCOPED_WRITE_LOCK ( m_connectionsLock , lock2 ) ;
m_connections . clear ( ) ;
2024-05-31 02:18:27 -04:00
m_connectionsIt = m_connections . end ( ) ;
2024-03-08 18:31:48 -05:00
}
FlushWork ( ) ;
2023-11-29 18:47:11 -05:00
}
2024-08-14 11:43:45 -04:00
bool NetworkClient : : StartListen ( NetworkBackend & backend , u16 port , const tchar * ip )
2023-11-29 18:47:11 -05:00
{
2024-08-14 11:43:45 -04:00
backend . StartListen ( m_logger , port , ip , [ & ] ( void * connection , const sockaddr & remoteSockAddr )
2023-11-29 18:47:11 -05:00
{
return AddConnection ( backend , connection , nullptr ) ;
} ) ;
return true ;
}
bool NetworkClient : : SetConnectionCount ( u32 count )
{
StackBinaryWriter < 64 > writer ;
NetworkMessage msg ( * this , SystemServiceId , SystemMessageType_SetConnectionCount , writer ) ; // Connection count
writer . WriteU32 ( count ) ;
return msg . Send ( ) ;
}
bool NetworkClient : : SendKeepAlive ( )
{
StackBinaryWriter < 64 > writer ;
NetworkMessage msg ( * this , SystemServiceId , SystemMessageType_KeepAlive , writer ) ;
2024-04-18 01:10:16 -04:00
writer . WriteByte ( 0 ) ; // Need to have a body
2023-11-29 18:47:11 -05:00
return msg . Send ( ) ;
}
bool NetworkClient : : IsConnected ( u32 waitTimeoutMs )
{
return m_isConnected . IsSet ( waitTimeoutMs ) ;
}
2023-12-14 13:31:47 -05:00
bool NetworkClient : : IsOrWasConnected ( u32 waitTimeoutMs )
{
return m_isOrWasConnected . IsSet ( waitTimeoutMs ) ;
}
2023-11-29 18:47:11 -05:00
void NetworkClient : : PrintSummary ( Logger & logger )
{
2024-02-26 01:14:42 -05:00
SCOPED_READ_LOCK ( m_connectionsLock , lock ) ;
2024-02-22 01:56:35 -05:00
u32 connectionsCount = u32 ( m_connections . size ( ) ) ;
lock . Leave ( ) ;
2023-11-29 18:47:11 -05:00
logger . Info ( TC ( " ----- Uba client stats summary ------ " ) ) ;
2024-03-03 21:07:10 -05:00
logger . Info ( TC ( " SendTotal %8u %9s " ) , m_sendTimer . count . load ( ) , TimeToText ( m_sendTimer . time ) . str ) ;
logger . Info ( TC ( " Bytes %9s " ) , BytesToText ( m_sendBytes ) . str ) ;
logger . Info ( TC ( " RecvTotal %8u %9s " ) , m_recvCount . load ( ) , BytesToText ( m_recvBytes ) . str ) ;
2023-11-29 18:47:11 -05:00
if ( m_cryptoKey )
{
logger . Info ( TC ( " EncryptTotal %8u %9s " ) , m_encryptTimer . count . load ( ) , TimeToText ( m_encryptTimer . time ) . str ) ;
logger . Info ( TC ( " DecryptTotal %8u %9s " ) , m_decryptTimer . count . load ( ) , TimeToText ( m_decryptTimer . time ) . str ) ;
}
logger . Info ( TC ( " MaxActiveMessages %8u " ) , m_activeMessageIdMax ) ;
2024-02-22 01:56:35 -05:00
logger . Info ( TC ( " Connections %8u " ) , connectionsCount ) ;
2023-11-29 18:47:11 -05:00
logger . Info ( TC ( " SendSize Set/Max %9s %9s " ) , BytesToText ( m_sendSize ) . str , BytesToText ( SendMaxSize ) . str ) ;
logger . Info ( TC ( " " ) ) ;
}
void NetworkClient : : RegisterOnConnected ( const OnConnectedFunction & function )
{
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_onConnectedFunctionsLock , lock ) ;
2023-11-29 18:47:11 -05:00
m_onConnectedFunctions . push_back ( function ) ;
2024-07-04 10:58:32 -04:00
if ( ! m_isConnected . IsSet ( 0 ) )
2023-11-29 18:47:11 -05:00
return ;
lock . Leave ( ) ;
function ( ) ;
}
void NetworkClient : : RegisterOnDisconnected ( const OnDisconnectedFunction & function )
{
2024-03-03 21:07:10 -05:00
SCOPED_WRITE_LOCK ( m_onDisconnectedFunctionsLock , lock ) ;
2023-11-29 18:47:11 -05:00
m_onDisconnectedFunctions . push_back ( function ) ;
}
void NetworkClient : : RegisterOnVersionMismatch ( const OnVersionMismatchFunction & function )
{
m_versionMismatchFunction = function ;
}
void NetworkClient : : InvokeVersionMismatch ( const CasKey & exeKey , const CasKey & dllKey )
{
if ( m_versionMismatchFunction )
m_versionMismatchFunction ( exeKey , dllKey ) ;
}
u64 NetworkClient : : GetMessageHeaderSize ( )
{
return SendHeaderSize ;
}
u64 NetworkClient : : GetMessageReceiveHeaderSize ( )
{
return ReceiveHeaderSize ;
}
u64 NetworkClient : : GetMessageMaxSize ( )
{
return m_sendSize ;
}
2024-03-03 21:07:10 -05:00
NetworkBackend * NetworkClient : : GetFirstConnectionBackend ( )
{
SCOPED_READ_LOCK ( m_connectionsLock , connectionLock ) ;
if ( m_connections . empty ( ) )
return nullptr ;
return m_connections . front ( ) . backend ;
}
2024-07-16 20:07:03 -04:00
void NetworkClient : : OnDisconnected ( Connection & connection , u32 reason )
2023-11-29 18:47:11 -05:00
{
if ( connection . connected . exchange ( 0 ) = = 1 )
{
2024-07-16 20:07:03 -04:00
m_logger . Detail ( TC ( " Disconnected from server... (0x%p) (%u) " ) , connection . backendConnection , reason ) ;
2023-11-29 18:47:11 -05:00
connection . backend - > Shutdown ( connection . backendConnection ) ;
if ( m_connectionCount . fetch_sub ( 1 ) = = 1 )
{
m_isConnected . Reset ( ) ;
2024-03-03 21:07:10 -05:00
SCOPED_READ_LOCK ( m_onDisconnectedFunctionsLock , lock ) ;
2023-11-29 18:47:11 -05:00
for ( auto & f : m_onDisconnectedFunctions )
f ( ) ;
}
}
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_activeMessagesLock , lock ) ;
2023-11-29 18:47:11 -05:00
for ( auto m : m_activeMessages )
{
if ( m & & m - > m_connection = = & connection )
{
2024-06-14 17:41:02 -04:00
m - > m_error = 3 ;
2024-03-03 21:07:10 -05:00
m - > Done ( false ) ;
2023-11-29 18:47:11 -05:00
}
}
}
bool NetworkClient : : Send ( NetworkMessage & message , void * response , u32 responseCapacity , bool async )
{
2024-02-26 01:14:42 -05:00
SCOPED_READ_LOCK ( m_connectionsLock , connectionLock ) ;
SCOPED_WRITE_LOCK ( m_connectionsItLock , connectionItLock ) ;
2023-11-29 18:47:11 -05:00
if ( m_connectionsIt = = m_connections . end ( ) )
2024-06-14 17:41:02 -04:00
{
2024-07-04 12:56:04 -04:00
if ( m_isDisconnecting )
message . m_error = 11 ;
else if ( ! m_connections . empty ( ) )
message . m_error = 12 ; // should never happen
else
message . m_error = 6 ;
2023-11-29 18:47:11 -05:00
return false ;
2024-06-14 17:41:02 -04:00
}
2023-11-29 18:47:11 -05:00
Connection & connection = * m_connectionsIt ;
+ + m_connectionsIt ;
if ( m_connectionsIt = = m_connections . end ( ) )
m_connectionsIt = m_connections . begin ( ) ;
connectionItLock . Leave ( ) ;
connectionLock . Leave ( ) ;
message . m_response = response ;
message . m_responseCapacity = responseCapacity ;
message . m_connection = & connection ;
2024-03-14 18:21:41 -04:00
BinaryWriter & writer = * message . m_sendWriter ;
2023-11-29 18:47:11 -05:00
u16 messageId = 0 ;
2024-07-22 19:08:56 -04:00
Event gotResponse ;
2023-11-29 18:47:11 -05:00
2024-07-22 19:11:51 -04:00
2023-11-29 18:47:11 -05:00
if ( response )
{
2024-07-22 19:11:51 -04:00
if ( ! async )
{
if ( ! gotResponse . Create ( true ) )
{
m_logger . Error ( TC ( " Failed to create event, this should not happen?!? " ) ) ;
message . m_error = 13 ;
OnDisconnected ( connection , 13 ) ;
return false ;
}
}
2023-11-29 18:47:11 -05:00
while ( true )
{
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_activeMessagesLock , lock ) ;
2023-11-29 18:47:11 -05:00
if ( m_availableMessageIds . empty ( ) )
{
if ( ! connection . connected )
2024-06-14 17:41:02 -04:00
{
message . m_error = 7 ;
2023-11-29 18:47:11 -05:00
return false ;
2024-06-14 17:41:02 -04:00
}
2023-11-29 18:47:11 -05:00
if ( m_activeMessageIdMax = = 65534 )
{
lock . Leave ( ) ;
m_logger . Info ( TC ( " Reached max limit of active message ids (65534). Waiting 1 second " ) ) ;
Sleep ( 100u + u32 ( rand ( ) ) % 900u ) ;
continue ;
}
messageId = m_activeMessageIdMax + + ;
if ( m_activeMessages . size ( ) < m_activeMessageIdMax )
m_activeMessages . resize ( size_t ( m_activeMessageIdMax ) + 1024 ) ;
}
else
{
messageId = m_availableMessageIds . back ( ) ;
m_availableMessageIds . pop_back ( ) ;
}
2024-02-21 02:52:38 -05:00
UBA_ASSERT ( ! m_activeMessages [ messageId ] ) ;
m_activeMessages [ messageId ] = & message ;
2024-03-03 21:07:10 -05:00
message . m_id = messageId ;
message . m_sendContext . flags = NetworkBackend : : SendFlags_ExternalWait ;
if ( ! async )
{
UBA_ASSERT ( ! message . m_doneFunc ) ;
message . m_doneUserData = & gotResponse ;
message . m_doneFunc = [ ] ( bool error , void * userData ) { ( ( Event * ) userData ) - > Set ( ) ; } ;
}
2023-11-29 18:47:11 -05:00
break ;
}
}
UBA_ASSERT ( messageId < 65535 ) ;
u32 sendSize = u32 ( writer . GetPosition ( ) ) ;
u8 * data = writer . GetData ( ) ;
data [ 1 ] = messageId > > 8 ;
2024-04-10 20:21:25 -04:00
u32 dataSize = sendSize - 6 ;
UBA_ASSERTF ( dataSize , TC ( " NetworkMessage must have data size of at least 1. " ) ) ;
* ( u32 * ) ( data + 2 ) = dataSize | u32 ( messageId ) < < 24 ;
2023-11-29 18:47:11 -05:00
//m_logger.Debug(TC("Send: %u, %u, %u, %u"), data[0], data[1], data[2], sendSize - 7);
u32 bodySize = sendSize - SendHeaderSize ;
if ( m_cryptoKey & & bodySize )
{
TimerScope ts ( m_encryptTimer ) ;
if ( ! Crypto : : Encrypt ( m_logger , m_cryptoKey , data + SendHeaderSize , bodySize ) )
{
2024-06-14 17:41:02 -04:00
message . m_error = 8 ;
2024-07-16 20:07:03 -04:00
OnDisconnected ( connection , 8 ) ;
2023-11-29 18:47:11 -05:00
return false ;
}
}
2024-03-03 21:07:10 -05:00
m_sendBytes + = sendSize ;
2024-02-28 00:30:17 -05:00
2023-11-29 18:47:11 -05:00
{
2024-03-03 21:07:10 -05:00
TimerScope ts ( m_sendTimer ) ;
2023-11-29 18:47:11 -05:00
if ( ! connection . backend - > Send ( m_logger , connection . backendConnection , data , sendSize , message . m_sendContext ) )
{
2024-06-14 17:41:02 -04:00
message . m_error = 9 ;
2024-07-16 20:07:03 -04:00
OnDisconnected ( connection , 9 ) ;
2023-11-29 18:47:11 -05:00
return false ;
}
}
if ( async )
return true ;
if ( response )
{
2024-07-22 18:35:42 -04:00
u64 waitStart = GetTime ( ) ;
2023-11-29 18:47:11 -05:00
u32 timeoutMs = 10 * 60 * 1000 ;
2024-02-28 00:30:17 -05:00
if ( ! gotResponse . IsSet ( timeoutMs ) )
2023-11-29 18:47:11 -05:00
{
2024-07-24 15:26:01 -04:00
m_logger . Error ( TC ( " Timed out after %s waiting for message response from server. " ) , TimeToText ( GetTime ( ) - waitStart , true ) . str ) ;
2024-06-14 17:41:02 -04:00
message . m_error = 4 ;
2023-11-29 18:47:11 -05:00
}
else if ( m_cryptoKey & & ! message . m_error & & message . m_responseSize )
{
TimerScope ts ( m_decryptTimer ) ;
if ( ! Crypto : : Decrypt ( m_logger , m_cryptoKey , ( u8 * ) message . m_response , message . m_responseSize ) )
2024-06-14 17:41:02 -04:00
message . m_error = 5 ;
2023-11-29 18:47:11 -05:00
}
}
return ! message . m_error ;
}
void NetworkClient : : ReturnMessageId ( u16 id )
{
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_activeMessagesLock , lock ) ;
2023-11-29 18:47:11 -05:00
m_availableMessageIds . push_back ( id ) ;
m_activeMessages [ id ] = nullptr ;
}
const tchar * NetworkClient : : SetGetPrefix ( const tchar * originalPrefix )
{
CreateGuid ( m_uid ) ;
StringBuffer < 512 > b ;
b . Appendf ( TC ( " %s (%s) " ) , originalPrefix , GuidToString ( m_uid ) . str ) ;
m_prefix = b . data ;
return m_prefix . c_str ( ) ;
}
NetworkMessage : : NetworkMessage ( NetworkClient & client , u8 serviceId , u8 messageType , BinaryWriter & sendWriter )
{
2024-03-14 18:21:41 -04:00
Init ( client , serviceId , messageType , sendWriter ) ;
}
NetworkMessage : : ~ NetworkMessage ( )
{
UBA_ASSERT ( ! m_id ) ;
}
void NetworkMessage : : Init ( NetworkClient & client , u8 serviceId , u8 messageType , BinaryWriter & sendWriter )
{
m_client = & client ;
m_sendWriter = & sendWriter ;
2023-11-29 18:47:11 -05:00
// Header (SendHeaderSize):
// 1 byte - 2 bits for serviceid, 6 bits for messagetype
// 2 byte - message id
// 3 byte - message size
UBA_ASSERT ( sendWriter . GetPosition ( ) = = 0 ) ;
UBA_ASSERT ( ( serviceId & 0b11 ) = = serviceId ) ;
UBA_ASSERT ( ( messageType & 0b111111 ) = = messageType ) ;
u8 * data = sendWriter . AllocWrite ( SendHeaderSize ) ;
data [ 0 ] = u8 ( serviceId < < 6 ) | messageType ;
}
bool NetworkMessage : : Send ( )
{
2024-03-14 18:21:41 -04:00
return m_client - > Send ( * this , nullptr , 0 , false ) ;
2023-11-29 18:47:11 -05:00
}
bool NetworkMessage : : Send ( BinaryReader & response )
{
2024-03-14 18:21:41 -04:00
if ( ! m_client - > Send ( * this , ( u8 * ) response . GetPositionData ( ) , u32 ( response . GetLeft ( ) ) , false ) )
2023-11-29 18:47:11 -05:00
return false ;
response . SetSize ( response . GetPosition ( ) + m_responseSize ) ;
return true ;
}
bool NetworkMessage : : Send ( BinaryReader & response , Timer & outTimer )
{
TimerScope ts ( outTimer ) ;
bool res = Send ( response ) ;
return res ;
}
2024-02-28 00:30:17 -05:00
bool NetworkMessage : : SendAsync ( BinaryReader & response , DoneFunc * func , void * userData )
2023-11-29 18:47:11 -05:00
{
2024-02-28 00:30:17 -05:00
UBA_ASSERT ( ! m_doneFunc ) ;
m_doneFunc = func ;
m_doneUserData = userData ;
2024-03-14 18:21:41 -04:00
return m_client - > Send ( * this , ( u8 * ) response . GetPositionData ( ) , u32 ( response . GetLeft ( ) ) , true ) ;
2023-11-29 18:47:11 -05:00
}
2024-02-28 00:30:17 -05:00
bool NetworkMessage : : ProcessAsyncResults ( BinaryReader & response )
2023-11-29 18:47:11 -05:00
{
if ( m_error )
return false ;
2024-03-14 18:21:41 -04:00
if ( m_client - > m_cryptoKey )
2023-11-29 18:47:11 -05:00
{
UBA_ASSERT ( ! response . GetPosition ( ) ) ;
2024-03-14 18:21:41 -04:00
TimerScope ts ( m_client - > m_decryptTimer ) ;
if ( ! Crypto : : Decrypt ( m_client - > m_logger , m_client - > m_cryptoKey , ( u8 * ) m_response , m_responseSize ) )
2024-06-14 17:41:02 -04:00
{
m_error = 10 ;
2023-11-29 18:47:11 -05:00
return false ;
2024-06-14 17:41:02 -04:00
}
2023-11-29 18:47:11 -05:00
}
response . SetSize ( response . GetPosition ( ) + m_responseSize ) ;
return true ;
}
2024-02-28 00:30:17 -05:00
2024-03-03 21:07:10 -05:00
void NetworkMessage : : Done ( bool shouldLock )
2024-02-28 00:30:17 -05:00
{
2024-03-03 21:07:10 -05:00
bool hasId = false ;
auto returnId = [ & ] ( )
{
if ( m_id )
{
2024-03-14 18:21:41 -04:00
m_client - > m_availableMessageIds . push_back ( m_id ) ;
m_client - > m_activeMessages [ m_id ] = nullptr ;
2024-03-03 21:07:10 -05:00
m_id = 0 ;
hasId = true ;
}
} ;
if ( shouldLock )
2024-02-28 00:30:17 -05:00
{
2024-03-14 18:21:41 -04:00
SCOPED_WRITE_LOCK ( m_client - > m_activeMessagesLock , lock ) ;
2024-03-03 21:07:10 -05:00
returnId ( ) ;
2024-02-28 00:30:17 -05:00
}
2024-03-03 21:07:10 -05:00
else
{
returnId ( ) ;
}
if ( hasId )
2024-06-14 17:41:02 -04:00
m_doneFunc ( m_error ! = 0 , m_doneUserData ) ;
2024-02-28 00:30:17 -05:00
}
2023-11-29 18:47:11 -05:00
}