2023-11-29 18:47:11 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "UbaNetworkServer.h"
# include "UbaCrypto.h"
# include "UbaBinaryReaderWriter.h"
# include "UbaPlatform.h"
namespace uba
{
2024-06-14 02:35:27 -04:00
void NetworkServerCreateInfo : : Apply ( Config & config )
{
}
2024-02-19 02:01:21 -05:00
struct NetworkServer : : WorkerContext
{
WorkerContext ( NetworkServer & s ) : server ( s ) , workAvailable ( false )
{
writeMemSize = server . m_sendSize ;
writeMem = new u8 [ writeMemSize ] ;
}
~ WorkerContext ( )
{
delete [ ] writeMem ;
}
NetworkServer & server ;
Event workAvailable ;
u8 * writeMem = nullptr ;
u32 writeMemSize = 0 ;
Vector < u8 > buffer ;
Connection * connection = nullptr ;
u32 dataSize = 0 ;
u8 serviceId = 0 ;
u8 messageType = 0 ;
u16 id = 0 ;
} ;
2023-11-29 18:47:11 -05:00
class NetworkServer : : Worker
{
public :
2024-02-19 02:01:21 -05:00
Worker ( ) { }
2023-11-29 18:47:11 -05:00
~ Worker ( )
{
2024-02-19 02:01:21 -05:00
UBA_ASSERT ( ! m_inUse ) ;
m_context - > connection = nullptr ;
2023-11-29 18:47:11 -05:00
m_loop = false ;
2024-02-19 02:01:21 -05:00
m_context - > workAvailable . Set ( ) ;
m_thread . Wait ( ) ;
delete m_context ;
m_context = nullptr ;
2023-11-29 18:47:11 -05:00
}
void Start ( NetworkServer & server )
{
2024-02-19 02:01:21 -05:00
m_context = new WorkerContext ( server ) ;
2023-11-29 18:47:11 -05:00
m_loop = true ;
m_thread . Start ( [ & ] ( ) { ThreadWorker ( server ) ; return 0 ; } ) ;
}
2024-02-19 02:01:21 -05:00
void Stop ( NetworkServer & server )
{
m_loop = false ;
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( server . m_availableWorkersLock , lock ) ;
2024-02-19 02:01:21 -05:00
while ( m_inUse )
{
2024-02-21 16:49:26 -05:00
m_context - > workAvailable . Set ( ) ;
2024-02-19 02:01:21 -05:00
lock . Leave ( ) ;
2024-02-21 16:49:26 -05:00
if ( m_thread . Wait ( 5 ) )
break ;
2024-02-19 02:01:21 -05:00
lock . Enter ( ) ;
}
}
2023-11-29 18:47:11 -05:00
void ThreadWorker ( NetworkServer & server ) ;
2024-02-19 02:01:21 -05:00
void Update ( WorkerContext & context , bool signalAvailable ) ;
2023-11-29 18:47:11 -05:00
void DoAdditionalWorkAndSignalAvailable ( NetworkServer & server ) ;
Worker * m_nextWorker = nullptr ;
Worker * m_prevWorker = nullptr ;
2024-02-19 02:01:21 -05:00
WorkerContext * m_context = nullptr ;
2024-02-21 16:49:26 -05:00
Atomic < bool > m_loop ;
Atomic < bool > m_inUse ;
2023-11-29 18:47:11 -05:00
Thread m_thread ;
Worker ( const Worker & ) = delete ;
} ;
2024-02-19 02:01:21 -05:00
thread_local NetworkServer : : Worker * t_worker ;
2023-11-29 18:47:11 -05:00
class NetworkServer : : Connection
{
public :
2024-06-10 23:37:00 -04:00
Connection ( NetworkServer & server , NetworkBackend & backend , void * backendConnection , const sockaddr & remoteSockAddr , bool requiresCrypto , CryptoKey cryptoKey , u32 id )
2023-11-29 18:47:11 -05:00
: m_server ( server )
, m_backend ( backend )
, m_remoteSockAddr ( remoteSockAddr )
, m_cryptoKey ( cryptoKey )
2024-02-22 01:56:35 -05:00
, m_disconnectCallbackCalled ( true )
2024-03-03 21:07:10 -05:00
, m_id ( id )
2023-11-29 18:47:11 -05:00
, m_backendConnection ( backendConnection )
{
m_activeWorkerCount = 1 ;
2024-03-03 21:07:10 -05:00
m_backend . SetDisconnectCallback ( m_backendConnection , this , [ ] ( void * context , const Guid & connectionUid , void * connection )
2023-11-29 18:47:11 -05:00
{
auto & conn = * ( Connection * ) context ;
conn . Disconnect ( ) ;
2024-02-22 01:56:35 -05:00
conn . m_disconnectCallbackCalled . Set ( ) ;
2023-11-29 18:47:11 -05:00
} ) ;
m_backend . SetDataSentCallback ( m_backendConnection , this , [ ] ( void * context , u32 bytes )
{
auto & conn = * ( Connection * ) context ;
if ( auto c = conn . m_client )
2023-12-15 16:57:54 -05:00
c - > recvBytes + = bytes ;
2023-11-29 18:47:11 -05:00
conn . m_server . m_sendBytes + = bytes ;
} ) ;
m_backend . SetRecvTimeout ( m_backendConnection , m_server . m_receiveTimeoutMs ) ;
2024-06-10 23:37:00 -04:00
if ( requiresCrypto )
{
m_backend . SetAllowLessThanBodySize ( m_backendConnection , true ) ;
2023-11-29 18:47:11 -05:00
m_backend . SetRecvCallbacks ( m_backendConnection , this , 0 , ReceiveHandshakeHeader , ReceiveHandshakeBody , TC ( " ReceiveHandshake " ) ) ;
2024-06-10 23:37:00 -04:00
}
2023-11-29 18:47:11 -05:00
else
m_backend . SetRecvCallbacks ( m_backendConnection , this , 4 , ReceiveVersion , nullptr , TC ( " ReceiveVersion " ) ) ;
}
2024-02-22 01:56:35 -05:00
~ Connection ( )
{
Stop ( ) ;
if ( m_cryptoKey )
Crypto : : DestroyKey ( m_cryptoKey ) ;
}
2023-11-29 18:47:11 -05:00
void Disconnect ( )
{
if ( m_disconnectCalled . fetch_add ( 1 ) ! = 0 )
return ;
SetShouldDisconnect ( ) ;
if ( - - m_activeWorkerCount = = 0 ) // Will disconnect in send if there are active workers
TestDisconnect ( ) ;
}
2023-12-19 13:29:44 -05:00
bool Stop ( )
2023-11-29 18:47:11 -05:00
{
Disconnect ( ) ;
2024-02-22 01:56:35 -05:00
2023-11-29 18:47:11 -05:00
u64 startTimer = GetTime ( ) ;
while ( m_activeWorkerCount )
{
if ( TimeToMs ( GetTime ( ) - startTimer ) > 3000 )
{
2023-12-19 13:29:44 -05:00
m_server . m_logger . Error ( TC ( " Connection has waited 3 seconds to stop... something is stuck (Active worker count: %u). Need someone to attach a debugger to process to get callstacks " ) , m_activeWorkerCount . load ( ) ) ;
return false ;
2023-11-29 18:47:11 -05:00
}
Sleep ( 1 ) ;
}
2024-02-22 01:56:35 -05:00
2024-02-22 15:02:42 -05:00
if ( ! m_disconnectCallbackCalled . IsSet ( 60000 ) ) // This should never time out!
2024-03-03 20:51:45 -05:00
m_server . m_logger . Warning ( TC ( " This should never happen!! Unknown consequences " ) ) ;
2023-12-19 13:29:44 -05:00
return true ;
2023-11-29 18:47:11 -05:00
}
2024-02-14 01:23:19 -05:00
bool SendInitialResponse ( u8 value )
2023-11-29 18:47:11 -05:00
{
2024-02-14 01:23:19 -05:00
u8 data [ 32 ] ;
* data = value ;
* ( Guid * ) ( data + 1 ) = m_server . m_uid ;
2023-11-29 18:47:11 -05:00
NetworkBackend : : SendContext context ( NetworkBackend : : SendFlags_Async ) ;
2024-02-14 01:23:19 -05:00
return m_backend . Send ( m_server . m_logger , m_backendConnection , data , 1 + sizeof ( Guid ) , context ) ;
2023-11-29 18:47:11 -05:00
}
2024-03-03 21:07:10 -05:00
static bool ReceiveHandshakeHeader ( void * context , const Guid & connectionUid , u8 * headerData , void * & outBodyContext , u8 * & outBodyData , u32 & outBodySize )
2023-11-29 18:47:11 -05:00
{
u8 * handshakeData = new u8 [ sizeof ( EncryptionHandshakeString ) ] ;
outBodyData = handshakeData ;
outBodySize = sizeof ( EncryptionHandshakeString ) ;
return true ;
}
static bool ReceiveHandshakeBody ( void * context , bool recvError , u8 * headerData , void * bodyContext , u8 * bodyData , u32 bodySize )
{
auto & conn = * ( Connection * ) context ;
u8 * handshakeData = bodyData ;
auto g = MakeGuard ( [ handshakeData ] ( ) { delete [ ] handshakeData ; } ) ;
2024-06-10 23:37:00 -04:00
auto & logger = conn . m_server . m_logger ;
2023-11-29 18:47:11 -05:00
2024-06-10 23:37:00 -04:00
if ( bodySize ! = sizeof ( EncryptionHandshakeString ) )
return logger . Error ( TC ( " Crypto mismatch... " ) ) ;
2023-11-29 18:47:11 -05:00
2024-06-10 23:37:00 -04:00
auto TestHandshake = [ & ] ( CryptoKey key )
{
u8 temp [ sizeof ( EncryptionHandshakeString ) ] ;
memcpy ( temp , handshakeData , sizeof ( temp ) ) ;
if ( ! Crypto : : Decrypt ( logger , key , temp , sizeof ( EncryptionHandshakeString ) ) )
return false ;
return memcmp ( temp , EncryptionHandshakeString , sizeof ( EncryptionHandshakeString ) ) = = 0 ;
} ;
if ( conn . m_cryptoKey ! = InvalidCryptoKey )
{
if ( ! TestHandshake ( conn . m_cryptoKey ) )
return logger . Error ( TC ( " Crypto mismatch... " ) ) ;
}
else
{
SCOPED_WRITE_LOCK ( conn . m_server . m_cryptoKeysLock , lock ) ;
auto & keys = conn . m_server . m_cryptoKeys ;
u64 time = GetTime ( ) ;
for ( auto it = keys . begin ( ) ; it ! = keys . end ( ) ; )
{
auto & entry = * it ;
if ( entry . expirationTime < time )
{
it = keys . erase ( it ) ;
continue ;
}
+ + it ;
CryptoKey key = Crypto : : DuplicateKey ( logger , entry . key ) ;
auto keyGuard = MakeGuard ( [ & ] ( ) { Crypto : : DestroyKey ( key ) ; } ) ;
if ( ! TestHandshake ( key ) )
continue ;
keyGuard . Cancel ( ) ;
conn . m_cryptoKey = key ;
break ;
}
if ( conn . m_cryptoKey = = InvalidCryptoKey )
return logger . Error ( TC ( " Crypto mismatch... " ) ) ;
}
conn . m_backend . SetAllowLessThanBodySize ( conn . m_backendConnection , false ) ;
2023-11-29 18:47:11 -05:00
conn . m_backend . SetRecvCallbacks ( conn . m_backendConnection , & conn , 4 , ReceiveVersion , nullptr , TC ( " ReceiveVersion " ) ) ;
return true ;
}
2024-03-03 21:07:10 -05:00
static bool ReceiveVersion ( void * context , const Guid & connectionUid , u8 * headerData , void * & outBodyContext , u8 * & outBodyData , u32 & outBodySize )
2023-11-29 18:47:11 -05:00
{
auto & conn = * ( Connection * ) context ;
u32 clientVersion = * ( u32 * ) headerData ;
if ( clientVersion ! = SystemNetworkVersion )
{
2024-02-14 01:23:19 -05:00
conn . SendInitialResponse ( 1 ) ;
2023-11-29 18:47:11 -05:00
return false ;
}
conn . m_backend . SetRecvCallbacks ( conn . m_backendConnection , & conn , sizeof ( Guid ) , ReceiveClientUid , nullptr , TC ( " ReceiveClientUid " ) ) ;
return true ;
}
2024-03-03 21:07:10 -05:00
static bool ReceiveClientUid ( void * context , const Guid & connectionUid , u8 * headerData , void * & outBodyContext , u8 * & outBodyData , u32 & outBodySize )
2023-11-29 18:47:11 -05:00
{
auto & conn = * ( Connection * ) context ;
auto & server = conn . m_server ;
Guid clientUid = * ( Guid * ) headerData ;
if ( ! server . m_allowNewClients )
{
2024-02-26 01:14:42 -05:00
SCOPED_READ_LOCK ( server . m_clientsLock , clientsLock ) ;
2023-11-29 18:47:11 -05:00
bool found = false ;
for ( auto & kv : server . m_clients )
found | = kv . second . uid = = clientUid ;
if ( ! found )
{
2024-02-14 01:23:19 -05:00
conn . SendInitialResponse ( 3 ) ;
2023-11-29 18:47:11 -05:00
return false ;
}
}
constexpr u32 HeaderSize = 6 ;
conn . m_backend . SetRecvCallbacks ( conn . m_backendConnection , & conn , HeaderSize , ReceiveMessageHeader , ReceiveMessageBody , TC ( " ReceiveMessage " ) ) ;
2024-02-14 01:23:19 -05:00
if ( ! conn . SendInitialResponse ( 0 ) )
2023-11-29 18:47:11 -05:00
return false ;
2024-03-08 18:31:48 -05:00
SCOPED_WRITE_LOCK ( conn . m_shutdownLock , shutdownLock ) ;
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( server . m_clientsLock , clientsLock ) ;
2023-11-29 18:47:11 -05:00
u32 clientId = u32 ( server . m_clients . size ( ) + 1 ) ;
for ( auto & kv : server . m_clients )
if ( kv . second . uid = = clientUid )
clientId = kv . second . id ;
Client & client = server . m_clients . try_emplace ( clientId , clientUid , clientId ) . first - > second ;
clientsLock . Leave ( ) ;
conn . m_client = & client ;
if ( client . connectionCount . fetch_add ( 1 ) = = 0 )
{
if ( server . m_onConnectionFunction )
server . m_onConnectionFunction ( clientUid , clientId ) ;
2024-07-31 16:03:16 -04:00
server . m_logger . Detail ( TC ( " Client %u (%s) connected on connection %s " ) , clientId , GuidToString ( clientUid ) . str , GuidToString ( connectionUid ) . str ) ;
2023-11-29 18:47:11 -05:00
}
else
2024-07-31 16:03:16 -04:00
server . m_logger . Detail ( TC ( " Client %u (%s) additional connection %s connected " ) , clientId , GuidToString ( clientUid ) . str , GuidToString ( connectionUid ) . str ) ;
2023-11-29 18:47:11 -05:00
return true ;
}
2024-03-03 21:07:10 -05:00
static bool ReceiveMessageHeader ( void * context , const Guid & connectionUid , u8 * headerData , void * & outBodyContext , u8 * & outBodyData , u32 & outBodySize )
2023-11-29 18:47:11 -05:00
{
auto & conn = * ( Connection * ) context ;
u8 serviceIdAndMessageType = headerData [ 0 ] ;
u8 serviceId = serviceIdAndMessageType > > 6 ;
u8 messageType = serviceIdAndMessageType & 0b111111 ;
u16 messageId = u16 ( headerData [ 1 ] < < 8 ) | u16 ( ( * ( u32 * ) ( headerData + 2 ) & 0xff000000 ) > > 24 ) ;
u32 messageSize = * ( u32 * ) ( headerData + 2 ) & 0x00ffffff ;
2023-12-15 02:18:13 -05:00
UBA_ASSERTF ( messageSize < = SendMaxSize , TC ( " Got message size %u which is larger than max %u. Protocol error? " ) , messageSize , SendMaxSize ) ;
UBA_ASSERTF ( serviceId < sizeof ( NetworkServer : : m_workerFunctions ) , TC ( " Got message with service id %u which is out of range. Protocol error? " ) , serviceId ) ;
2023-11-29 18:47:11 -05:00
//m_logger.Debug(TC("Recv: %u, %u, %u, %u"), serviceId, messageType, id, size);
Worker * worker = conn . m_server . PopWorker ( ) ;
2024-02-19 02:01:21 -05:00
if ( ! worker )
return false ;
auto & wc = * worker - > m_context ;
wc . id = messageId ;
wc . serviceId = serviceId ;
wc . messageType = messageType ;
wc . dataSize = messageSize ;
wc . connection = & conn ; // this;
if ( wc . buffer . size ( ) < messageSize )
wc . buffer . resize ( size_t ( Min ( messageSize + 1024u , SendMaxSize ) ) ) ;
2023-11-29 18:47:11 -05:00
outBodyContext = worker ;
2024-02-19 02:01:21 -05:00
outBodyData = wc . buffer . data ( ) ;
2023-11-29 18:47:11 -05:00
outBodySize = messageSize ;
return true ;
}
static bool ReceiveMessageBody ( void * context , bool recvError , u8 * headerData , void * bodyContext , u8 * bodyData , u32 bodySize )
{
auto & conn = * ( Connection * ) context ;
auto worker = ( Worker * ) bodyContext ;
if ( recvError )
{
conn . m_server . PushWorker ( worker ) ;
return false ;
}
2024-02-19 02:01:21 -05:00
auto & wc = * worker - > m_context ;
2023-11-29 18:47:11 -05:00
2024-02-19 02:01:21 -05:00
conn . m_client - > sendBytes + = wc . dataSize ;
conn . m_server . m_recvBytes + = wc . dataSize ;
2023-11-29 18:47:11 -05:00
+ + conn . m_server . m_recvCount ;
+ + conn . m_activeWorkerCount ;
2024-02-19 02:01:21 -05:00
wc . workAvailable . Set ( ) ;
2023-11-29 18:47:11 -05:00
return true ;
}
void Send ( const void * data , u32 bytes )
{
TimerScope ts ( m_sendTimer ) ;
NetworkBackend : : SendContext context ;
if ( ! m_backend . Send ( m_server . m_logger , m_backendConnection , data , bytes , context ) )
SetShouldDisconnect ( ) ;
}
bool SetShouldDisconnect ( )
{
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_shutdownLock , lock ) ;
2023-11-29 18:47:11 -05:00
bool isConnected = ! m_shouldDisconnect ;
m_shouldDisconnect = true ;
return isConnected ;
}
void Release ( )
{
if ( - - m_activeWorkerCount = = 0 )
TestDisconnect ( ) ;
}
void TestDisconnect ( )
{
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_shutdownLock , lock ) ;
2023-11-29 18:47:11 -05:00
if ( ! m_shouldDisconnect )
return ;
if ( m_disconnected )
return ;
lock . Leave ( ) ;
2024-02-22 15:02:42 -05:00
m_backend . Shutdown ( m_backendConnection ) ;
2023-11-29 18:47:11 -05:00
if ( m_client & & m_client - > connectionCount . fetch_sub ( 1 ) = = 1 )
{
2024-03-08 18:31:48 -05:00
SCOPED_READ_LOCK ( m_server . m_onDisconnectFunctionsLock , l ) ;
2023-11-29 18:47:11 -05:00
for ( auto & entry : m_server . m_onDisconnectFunctions )
entry . function ( m_client - > uid , m_client - > id ) ;
2024-07-31 16:03:16 -04:00
m_server . m_logger . Detail ( TC ( " Client %u (%s) disconnected " ) , m_client - > id , GuidToString ( m_client - > uid ) . str ) ;
2023-11-29 18:47:11 -05:00
}
m_disconnected = true ;
}
NetworkServer & m_server ;
NetworkBackend & m_backend ;
ReaderWriterLock m_shutdownLock ;
Client * m_client = nullptr ;
sockaddr m_remoteSockAddr ;
CryptoKey m_cryptoKey ;
2024-02-22 01:56:35 -05:00
Event m_disconnectCallbackCalled ;
2023-11-29 18:47:11 -05:00
Atomic < int > m_activeWorkerCount ;
Atomic < int > m_disconnectCalled ;
2024-02-21 20:09:27 -05:00
Atomic < bool > m_disconnected ;
2024-03-03 21:07:10 -05:00
u32 m_id = 0 ;
2023-11-29 18:47:11 -05:00
bool m_shouldDisconnect = false ;
void * m_backendConnection = nullptr ;
Timer m_sendTimer ;
Timer m_encryptTimer ;
Timer m_decryptTimer ;
Connection ( const Connection & o ) = delete ;
void operator = ( const Connection & o ) = delete ;
} ;
const Guid & ConnectionInfo : : GetUid ( ) const
{
return ( ( NetworkServer : : Connection * ) internalData ) - > m_client - > uid ;
}
u32 ConnectionInfo : : GetId ( ) const
{
return ( ( NetworkServer : : Connection * ) internalData ) - > m_client - > id ;
}
bool ConnectionInfo : : GetName ( StringBufferBase & out ) const
{
# if PLATFORM_WINDOWS
auto & remoteSockAddr = ( ( NetworkServer : : Connection * ) internalData ) - > m_remoteSockAddr ;
if ( ! InetNtopW ( AF_INET , & remoteSockAddr , out . data , out . capacity ) )
return false ;
out . count = u32 ( wcslen ( out . data ) ) ;
return true ;
# else
UBA_ASSERT ( false ) ;
return false ;
# endif
}
2023-12-12 21:25:58 -05:00
bool ConnectionInfo : : ShouldDisconnect ( ) const
{
2024-02-22 01:56:35 -05:00
auto & conn = * ( NetworkServer : : Connection * ) internalData ;
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( conn . m_shutdownLock , lock ) ;
2024-02-22 01:56:35 -05:00
return conn . m_shouldDisconnect ;
2023-12-12 21:25:58 -05:00
}
2024-02-19 02:01:21 -05:00
void NetworkServer : : Worker : : Update ( WorkerContext & context , bool signalAvailable )
{
auto & server = context . server ;
auto sg = MakeGuard ( [ & ] ( )
{
if ( signalAvailable )
DoAdditionalWorkAndSignalAvailable ( server ) ;
} ) ;
// This is only additional work
if ( ! context . connection )
return ;
auto & connection = * context . connection ;
context . connection = nullptr ;
CryptoKey cryptoKey = connection . m_cryptoKey ;
if ( cryptoKey )
{
TimerScope ts ( connection . m_decryptTimer ) ;
if ( ! Crypto : : Decrypt ( server . m_logger , cryptoKey , context . buffer . data ( ) , context . dataSize ) )
{
connection . SetShouldDisconnect ( ) ;
connection . Release ( ) ;
return ;
}
}
BinaryReader reader ( context . buffer . data ( ) , 0 , context . dataSize ) ;
constexpr u32 HeaderSize = 5 ; // 2 byte id, 3 bytes size
constexpr u32 ErrorSize = 0xffffff ;
BinaryWriter writer ( context . writeMem , 0 , context . writeMemSize ) ;
u8 * idAndSizePtr = writer . AllocWrite ( HeaderSize ) ;
u32 size ;
WorkerRec & rec = server . m_workerFunctions [ context . serviceId ] ;
2024-02-26 01:35:40 -05:00
u32 workId = server . TrackWorkStart ( rec . toString ( context . messageType ) ) ;
2024-02-19 02:01:21 -05:00
2024-03-03 21:30:10 -05:00
MessageInfo mi ;
mi . type = context . messageType ;
mi . connectionId = connection . m_id ;
mi . messageId = context . id ;
2024-02-19 02:01:21 -05:00
if ( ! rec . func )
{
server . m_logger . Error ( TC ( " WORKER FUNCTION NOT FOUND. id: %u, serviceid: %u type: %s, client: %s " ) , context . id , context . serviceId , rec . toString ( context . messageType ) , GuidToString ( connection . m_client - > uid ) . str ) ;
connection . SetShouldDisconnect ( ) ;
size = ErrorSize ;
}
2024-03-03 21:30:10 -05:00
else if ( ! rec . func ( { & connection } , mi , reader , writer ) )
2024-02-19 02:01:21 -05:00
{
if ( connection . SetShouldDisconnect ( ) )
server . m_logger . Error ( TC ( " WORKER FUNCTION FAILED. id: %u, serviceid: %u type: %s, client: %s " ) , context . id , context . serviceId , rec . toString ( context . messageType ) , GuidToString ( connection . m_client - > uid ) . str ) ;
size = ErrorSize ;
}
else
{
size = u32 ( writer . GetPosition ( ) ) ;
}
2024-02-26 01:35:40 -05:00
server . TrackWorkEnd ( workId ) ;
2024-02-19 02:01:21 -05:00
2024-03-03 21:30:10 -05:00
if ( mi . messageId )
2024-02-19 02:01:21 -05:00
{
UBA_ASSERT ( size < ( 1 < < 24 ) ) ;
u32 bodySize = u32 ( size - HeaderSize ) ;
if ( cryptoKey & & size ! = ErrorSize & & bodySize )
{
TimerScope ts ( connection . m_encryptTimer ) ;
u8 * bodyData = writer . GetData ( ) + HeaderSize ;
if ( ! Crypto : : Encrypt ( server . m_logger , cryptoKey , bodyData , bodySize ) )
{
connection . SetShouldDisconnect ( ) ;
size = ErrorSize ;
bodySize = u32 ( size - HeaderSize ) ;
}
}
idAndSizePtr [ 0 ] = context . id > > 8 ;
* ( u32 * ) ( idAndSizePtr + 1 ) = bodySize | u32 ( context . id < < 24 ) ;
// This can happen for proxy servers in a valid situation
//if (size == ErrorSize)
// UBA_ASSERT(false);
connection . Send ( writer . GetData ( ) , size = = ErrorSize ? HeaderSize : size ) ;
}
connection . Release ( ) ;
}
2023-11-29 18:47:11 -05:00
void NetworkServer : : Worker : : ThreadWorker ( NetworkServer & server )
{
2024-02-26 01:29:10 -05:00
ElevateCurrentThreadPriority ( ) ;
2024-02-19 02:01:21 -05:00
t_worker = this ;
while ( m_context - > workAvailable . IsSet ( ~ 0u ) & & m_loop )
Update ( * m_context , true ) ;
t_worker = nullptr ;
2024-02-21 16:49:26 -05:00
if ( m_inUse ) // I have no idea how this can happen.. should not be possible. There is a path somewhere where it can leave while still being in use
server . PushWorker ( this ) ;
2023-11-29 18:47:11 -05:00
}
void NetworkServer : : Worker : : DoAdditionalWorkAndSignalAvailable ( NetworkServer & server )
{
while ( true )
{
while ( true )
{
AdditionalWork work ;
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( server . m_additionalWorkLock , lock ) ;
2023-11-29 18:47:11 -05:00
if ( server . m_additionalWork . empty ( ) )
break ;
2024-03-03 20:51:45 -05:00
work = std : : move ( server . m_additionalWork . front ( ) ) ;
2023-11-29 18:47:11 -05:00
server . m_additionalWork . pop_front ( ) ;
lock . Leave ( ) ;
2024-02-26 01:35:40 -05:00
u32 workId = server . TrackWorkStart ( work . desc . c_str ( ) ) ;
2023-11-29 18:47:11 -05:00
work . func ( ) ;
2024-02-26 01:35:40 -05:00
server . TrackWorkEnd ( workId ) ;
2023-11-29 18:47:11 -05:00
}
// Both locks needs to be taken to verify if additional work
// is present before making ourself available to avoid
// a race where AddWork would not see this thread in the
// available list after adding some work.
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( server . m_availableWorkersLock , lock1 ) ;
SCOPED_READ_LOCK ( server . m_additionalWorkLock , lock2 ) ;
2023-11-29 18:47:11 -05:00
// Verify there is not additional work while we hold both lock
// and only add ourself as available if no additional work is present.
if ( ! server . m_additionalWork . empty ( ) )
continue ;
server . PushWorkerNoLock ( this ) ;
break ;
}
}
const tchar * g_typeStr [ ] = { TC ( " 0 " ) , TC ( " 1 " ) , TC ( " 2 " ) , TC ( " 3 " ) , TC ( " 4 " ) , TC ( " 5 " ) , TC ( " 6 " ) , TC ( " 7 " ) , TC ( " 8 " ) , TC ( " 9 " ) , TC ( " 10 " ) , TC ( " 11 " ) , TC ( " 12 " ) } ;
static const tchar * GetMessageTypeToName ( u8 type )
{
if ( type < = 12 )
return g_typeStr [ type ] ;
return TC ( " NUMBER HIGHER THAN 12 " ) ;
}
NetworkServer : : NetworkServer ( bool & outCtorSuccess , const NetworkServerCreateInfo & info , const tchar * name )
: m_logger ( info . logWriter , name )
2024-02-19 02:01:21 -05:00
, m_workerAvailable ( true )
2023-11-29 18:47:11 -05:00
{
outCtorSuccess = true ;
2024-02-26 01:35:40 -05:00
u32 workerCount ;
if ( info . workerCount = = 0 )
workerCount = GetLogicalProcessorCount ( ) ;
else
workerCount = Min ( Max ( info . workerCount , ( u32 ) ( 1u ) ) , ( u32 ) ( 1024u ) ) ;
2024-02-19 02:01:21 -05:00
m_maxWorkerCount = workerCount ;
2023-11-29 18:47:11 -05:00
# if UBA_DEBUG
m_logger . Info ( TC ( " Created in DEBUG " ) ) ;
# endif
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_receiveTimeoutMs = info . receiveTimeoutSeconds * 1000 ;
m_workerFunctions [ SystemServiceId ] . toString = GetMessageTypeToName ;
2024-03-03 21:30:10 -05:00
m_workerFunctions [ SystemServiceId ] . func = [ this ] ( const ConnectionInfo & connectionInfo , MessageInfo & messageInfo , BinaryReader & reader , BinaryWriter & writer )
2023-11-29 18:47:11 -05:00
{
2024-03-03 21:30:10 -05:00
return HandleSystemMessage ( connectionInfo , messageInfo . type , reader , writer ) ;
2023-11-29 18:47:11 -05:00
} ;
2024-02-14 01:23:19 -05:00
if ( ! CreateGuid ( m_uid ) )
outCtorSuccess = false ;
2023-11-29 18:47:11 -05:00
}
NetworkServer : : ~ NetworkServer ( )
{
2024-03-08 18:31:48 -05:00
UBA_ASSERT ( m_connections . empty ( ) ) ;
FlushWorkers ( ) ;
2024-06-10 23:37:00 -04:00
for ( auto & entry : m_cryptoKeys )
Crypto : : DestroyKey ( entry . key ) ;
2023-11-29 18:47:11 -05:00
}
2024-06-10 23:37:00 -04:00
bool NetworkServer : : StartListen ( NetworkBackend & backend , u16 port , const tchar * ip , bool requiresCrypto )
2023-11-29 18:47:11 -05:00
{
2024-06-10 23:37:00 -04:00
return backend . StartListen ( m_logger , port , ip , [ this , & backend , requiresCrypto ] ( void * connection , const sockaddr & remoteSockAddr )
2023-11-29 18:47:11 -05:00
{
2024-06-10 23:37:00 -04:00
return AddConnection ( backend , connection , remoteSockAddr , requiresCrypto , InvalidCryptoKey ) ;
2023-11-29 18:47:11 -05:00
} ) ;
}
2024-03-08 18:31:48 -05:00
2023-11-29 18:47:11 -05:00
void NetworkServer : : DisallowNewClients ( )
{
m_allowNewClients = false ;
}
2024-03-08 18:31:48 -05:00
void NetworkServer : : DisconnectClients ( )
2023-11-29 18:47:11 -05:00
{
2024-02-19 02:01:21 -05:00
{
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_availableWorkersLock , lock ) ;
2024-02-19 02:01:21 -05:00
m_workersEnabled = false ;
m_workerAvailable . Set ( ) ;
}
2023-11-29 18:47:11 -05:00
{
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_addConnectionsLock , lock ) ;
2023-11-29 18:47:11 -05:00
m_addConnections . clear ( ) ;
}
{
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_connectionsLock , lock ) ;
2023-12-19 13:29:44 -05:00
bool success = true ;
2024-02-21 20:09:27 -05:00
for ( auto & c : m_connections )
2023-11-29 18:47:11 -05:00
{
2023-12-19 13:29:44 -05:00
success = c . Stop ( ) & & success ;
2024-06-02 18:14:50 -04:00
m_sendTimer + = c . m_sendTimer ;
m_encryptTimer + = c . m_encryptTimer ;
m_decryptTimer + = c . m_decryptTimer ;
2023-11-29 18:47:11 -05:00
}
2024-02-21 20:09:27 -05:00
lock . Leave ( ) ;
2023-12-19 13:29:44 -05:00
// If stopping connections fail we need to abort because we will most likely run into a deadlock when deleting the workers.
if ( ! success )
2024-03-08 18:31:48 -05:00
{
m_logger . Info ( TC ( " Failed to stop connection(s) in a graceful way. Will abort process " ) ) ;
2023-12-19 13:29:44 -05:00
abort ( ) ; // TODO: Does this produce core dump on windows?
2024-03-08 18:31:48 -05:00
}
2023-11-29 18:47:11 -05:00
}
2024-03-08 18:31:48 -05:00
FlushWorkers ( ) ;
2024-02-19 02:01:21 -05:00
2024-03-08 18:31:48 -05:00
SCOPED_WRITE_LOCK ( m_connectionsLock , lock ) ;
2024-02-21 20:42:33 -05:00
m_connections . clear ( ) ;
2024-06-07 16:29:55 -04:00
m_workersEnabled = true ;
2023-11-29 18:47:11 -05:00
}
2024-06-10 23:37:00 -04:00
bool NetworkServer : : RegisterCryptoKey ( const u8 * cryptoKey128 , u64 expirationTime )
{
CryptoKey key = Crypto : : CreateKey ( m_logger , cryptoKey128 ) ;
if ( key = = InvalidCryptoKey )
return false ;
SCOPED_WRITE_LOCK ( m_cryptoKeysLock , lock ) ;
2024-06-11 00:02:05 -04:00
m_cryptoKeys . push_back ( CryptoEntry { key , expirationTime } ) ;
2024-06-10 23:37:00 -04:00
return true ;
}
2023-11-29 18:47:11 -05:00
bool NetworkServer : : AddClient ( NetworkBackend & backend , const tchar * ip , u16 port , const u8 * cryptoKey128 )
{
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_addConnectionsLock , lock ) ;
2024-03-08 18:31:48 -05:00
if ( ! m_workersEnabled )
return false ;
2023-11-29 18:47:11 -05:00
for ( auto it = m_addConnections . begin ( ) ; it ! = m_addConnections . end ( ) ; )
{
if ( it - > Wait ( 0 ) )
it = m_addConnections . erase ( it ) ;
else
+ + it ;
}
CryptoKey cryptoKey = InvalidCryptoKey ;
if ( cryptoKey128 )
{
cryptoKey = Crypto : : CreateKey ( m_logger , cryptoKey128 ) ;
if ( cryptoKey = = InvalidCryptoKey )
return false ;
}
2023-11-30 12:58:02 -05:00
Event done ( true ) ;
bool success = false ;
m_addConnections . emplace_back ( [ this , & success , & done , & backend , ip2 = TString ( ip ) , port , cryptoKey ] ( )
2023-11-29 18:47:11 -05:00
{
2023-11-30 12:58:02 -05:00
// TODO: Should this retry?
success = backend . Connect ( m_logger , ip2 . c_str ( ) , [ this , & backend , cryptoKey ] ( void * connection , const sockaddr & remoteSocketAddr , bool * timedOut )
2023-11-29 18:47:11 -05:00
{
2024-06-10 23:37:00 -04:00
return AddConnection ( backend , connection , remoteSocketAddr , cryptoKey ! = InvalidCryptoKey , cryptoKey ) ;
2023-11-29 18:47:11 -05:00
} , port , nullptr ) ;
if ( ! success )
Crypto : : DestroyKey ( cryptoKey ) ;
2023-11-30 12:58:02 -05:00
done . Set ( ) ;
2023-11-29 18:47:11 -05:00
return 0 ;
} ) ;
2023-11-30 12:58:02 -05:00
done . IsSet ( ) ;
return success ;
2023-11-29 18:47:11 -05:00
}
void NetworkServer : : PrintSummary ( Logger & logger )
{
if ( ! m_maxActiveConnections )
return ;
2024-06-07 16:29:55 -04:00
m_maxCreatedWorkerCount = Max ( m_createdWorkerCount , m_maxCreatedWorkerCount ) ;
2023-11-29 18:47:11 -05:00
StringBuffer < > workers ;
2024-06-07 16:29:55 -04:00
workers . Appendf ( TC ( " %u/%u " ) , m_maxCreatedWorkerCount , m_maxWorkerCount ) ;
2023-11-29 18:47:11 -05:00
logger . Info ( TC ( " ----- Uba server stats summary ------ " ) ) ;
logger . Info ( TC ( " MaxActiveConnections %6u " ) , m_maxActiveConnections ) ;
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 . load ( ) ) . str ) ;
if ( m_encryptTimer . count | | m_decryptTimer . count )
{
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 ( " WorkerCount %9s " ) , workers . data ) ;
logger . Info ( TC ( " SendSize Set/Max %9s %9s " ) , BytesToText ( m_sendSize ) . str , BytesToText ( SendMaxSize ) . str ) ;
logger . Info ( TC ( " " ) ) ;
}
void NetworkServer : : RegisterService ( u8 serviceId , const WorkerFunction & function , TypeToNameFunction * typeToNameFunc )
{
UBA_ASSERTF ( serviceId ! = 0 , TC ( " ServiceId 0 is reserved by system " ) ) ;
WorkerRec & rec = m_workerFunctions [ serviceId ] ;
UBA_ASSERT ( ! rec . func ) ;
rec . func = function ;
rec . toString = typeToNameFunc ;
if ( ! typeToNameFunc )
rec . toString = GetMessageTypeToName ;
}
void NetworkServer : : UnregisterService ( u8 serviceId )
{
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_connectionsLock , lock ) ;
2023-11-29 18:47:11 -05:00
UBA_ASSERTF ( m_connections . empty ( ) , TC ( " Unregistering service while still having live connections " ) ) ;
WorkerRec & rec = m_workerFunctions [ serviceId ] ;
rec . func = { } ;
//rec.toString = nullptr; // Keep this for now, we want to be able to output stats
}
void NetworkServer : : RegisterOnClientConnected ( u8 id , const OnConnectionFunction & func )
{
UBA_ASSERT ( ! m_onConnectionFunction ) ;
m_onConnectionFunction = func ;
}
void NetworkServer : : UnregisterOnClientConnected ( u8 id )
{
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_connectionsLock , lock ) ;
2023-11-29 18:47:11 -05:00
UBA_ASSERT ( m_connections . empty ( ) ) ;
m_onConnectionFunction = { } ;
}
void NetworkServer : : RegisterOnClientDisconnected ( u8 id , const OnDisconnectFunction & func )
{
2024-03-08 18:31:48 -05:00
SCOPED_WRITE_LOCK ( m_onDisconnectFunctionsLock , l ) ;
2023-11-29 18:47:11 -05:00
m_onDisconnectFunctions . emplace_back ( OnDisconnectEntry { id , func } ) ;
}
void NetworkServer : : UnregisterOnClientDisconnected ( u8 id )
{
2024-03-08 18:31:48 -05:00
SCOPED_WRITE_LOCK ( m_onDisconnectFunctionsLock , l ) ;
2023-11-29 18:47:11 -05:00
for ( auto it = m_onDisconnectFunctions . begin ( ) ; it ! = m_onDisconnectFunctions . end ( ) ; + + it )
{
if ( it - > id ! = id )
continue ;
m_onDisconnectFunctions . erase ( it ) ;
return ;
}
}
2024-06-02 18:15:45 -04:00
void NetworkServer : : AddWork ( const Function < void ( ) > & work , u32 count , const tchar * desc , bool highPriority )
2023-11-29 18:47:11 -05:00
{
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_additionalWorkLock , lock ) ;
2023-11-29 18:47:11 -05:00
for ( u32 i = 0 ; i ! = count ; + + i )
{
2024-06-02 18:15:45 -04:00
if ( highPriority )
{
m_additionalWork . push_front ( { work } ) ;
if ( m_workTracker )
m_additionalWork . front ( ) . desc = desc ;
}
else
{
m_additionalWork . push_back ( { work } ) ;
if ( m_workTracker )
m_additionalWork . back ( ) . desc = desc ;
}
2023-11-29 18:47:11 -05:00
}
lock . Leave ( ) ;
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_availableWorkersLock , lock2 ) ;
2024-03-08 18:31:48 -05:00
if ( ! m_workersEnabled )
return ;
2024-03-05 11:32:25 -05:00
while ( count - - )
2023-11-29 18:47:11 -05:00
{
Worker * worker = PopWorkerNoLock ( ) ;
2024-02-19 02:01:21 -05:00
if ( ! worker )
break ;
UBA_ASSERT ( worker - > m_inUse ) ;
worker - > m_context - > connection = nullptr ;
worker - > m_context - > workAvailable . Set ( ) ;
2023-11-29 18:47:11 -05:00
}
}
2024-04-12 18:04:58 -04:00
void NetworkServer : : DoWork ( u32 count )
{
while ( count - - )
if ( ! DoAdditionalWork ( ) )
return ;
}
2023-11-29 18:47:11 -05:00
u32 NetworkServer : : GetWorkerCount ( )
{
2024-02-19 02:01:21 -05:00
return m_maxWorkerCount ;
2023-11-29 18:47:11 -05:00
}
2024-04-12 18:04:58 -04:00
MutableLogger & NetworkServer : : GetLogger ( )
{
return m_logger ;
}
2023-11-29 18:47:11 -05:00
u64 NetworkServer : : GetTotalSentBytes ( )
{
return m_sendBytes ;
}
u64 NetworkServer : : GetTotalRecvBytes ( )
{
return m_recvBytes ;
}
2024-03-03 20:51:45 -05:00
u32 NetworkServer : : GetConnectionCount ( )
{
SCOPED_READ_LOCK ( m_connectionsLock , lock ) ;
u32 count = 0 ;
for ( auto & con : m_connections )
if ( ! con . m_disconnected )
+ + count ;
return count ;
}
2023-11-29 18:47:11 -05:00
void NetworkServer : : GetClientStats ( ClientStats & out , u32 clientId )
{
2024-02-26 01:14:42 -05:00
SCOPED_READ_LOCK ( m_clientsLock , lock ) ;
2023-11-29 18:47:11 -05:00
auto findIt = m_clients . find ( clientId ) ;
if ( findIt = = m_clients . end ( ) )
return ;
Client & c = findIt - > second ;
out . send + = c . sendBytes ;
out . recv + = c . recvBytes ;
out . connectionCount + = c . connectionCount ;
}
bool NetworkServer : : DoAdditionalWork ( )
{
AdditionalWork work ;
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_additionalWorkLock , lock ) ;
2023-11-29 18:47:11 -05:00
if ( m_additionalWork . empty ( ) )
2024-02-19 02:01:21 -05:00
{
lock . Leave ( ) ;
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_availableWorkersLock , lock2 ) ;
2024-02-19 02:01:21 -05:00
if ( m_createdWorkerCount ! = m_maxWorkerCount )
return false ;
lock2 . Leave ( ) ;
2024-04-13 19:21:42 -04:00
2024-02-19 02:01:21 -05:00
auto worker = t_worker ;
2024-04-13 19:21:42 -04:00
if ( ! worker )
return false ;
2024-02-19 02:01:21 -05:00
auto oldContext = worker - > m_context ;
WorkerContext context ( * this ) ;
worker - > m_context = & context ;
PushWorker ( worker ) ;
bool workAvail = context . workAvailable . IsSet ( 10 ) ;
lock2 . Enter ( ) ;
if ( worker - > m_inUse )
{
lock2 . Leave ( ) ;
if ( ! workAvail )
context . workAvailable . IsSet ( ~ 0u ) ;
worker - > Update ( context , false ) ;
UBA_ASSERT ( worker - > m_inUse ) ;
}
else
{
// Take worker back from free list
if ( m_firstAvailableWorker = = worker )
m_firstAvailableWorker = worker - > m_nextWorker ;
else
worker - > m_prevWorker - > m_nextWorker = worker - > m_nextWorker ;
if ( worker - > m_nextWorker )
worker - > m_nextWorker - > m_prevWorker = worker - > m_prevWorker ;
worker - > m_prevWorker = nullptr ;
worker - > m_nextWorker = m_firstActiveWorker ;
if ( m_firstActiveWorker )
m_firstActiveWorker - > m_prevWorker = worker ;
m_firstActiveWorker = worker ;
worker - > m_inUse = true ;
}
worker - > m_context = oldContext ;
return true ;
}
2024-03-03 20:51:45 -05:00
work = std : : move ( m_additionalWork . front ( ) ) ;
2023-11-29 18:47:11 -05:00
m_additionalWork . pop_front ( ) ;
lock . Leave ( ) ;
2024-03-03 20:51:45 -05:00
TrackWorkScope tws ( * this , work . desc . c_str ( ) ) ;
2023-11-29 18:47:11 -05:00
work . func ( ) ;
return true ;
}
2024-03-03 21:30:10 -05:00
bool NetworkServer : : SendResponse ( const MessageInfo & info , const u8 * body , u32 bodySize )
{
UBA_ASSERT ( info . connectionId ) ;
UBA_ASSERT ( info . messageId ) ;
SCOPED_READ_LOCK ( m_connectionsLock , lock ) ;
Connection * found = nullptr ;
for ( auto & it : m_connections )
{
if ( it . m_id ! = info . connectionId )
continue ;
found = & it ;
break ;
}
if ( ! found )
return false ;
Connection & connection = * found ;
u8 buffer [ SendMaxSize ] ;
constexpr u32 HeaderSize = 5 ; // 2 byte id, 3 bytes size
BinaryWriter writer ( buffer , 0 , sizeof_array ( buffer ) ) ;
u8 * idAndSizePtr = writer . AllocWrite ( HeaderSize ) ;
2024-03-08 18:31:48 -05:00
if ( body )
2024-03-03 21:30:10 -05:00
{
2024-03-08 18:31:48 -05:00
writer . WriteBytes ( body , bodySize ) ;
if ( connection . m_cryptoKey & & bodySize )
2024-03-03 21:30:10 -05:00
{
2024-03-08 18:31:48 -05:00
TimerScope ts ( connection . m_encryptTimer ) ;
u8 * bodyData = writer . GetData ( ) + HeaderSize ;
if ( ! Crypto : : Encrypt ( m_logger , connection . m_cryptoKey , bodyData , bodySize ) )
{
connection . SetShouldDisconnect ( ) ;
return false ;
}
2024-03-03 21:30:10 -05:00
}
}
2024-03-08 18:31:48 -05:00
else
{
constexpr u32 ErrorSize = 0xffffff ;
bodySize = ErrorSize ;
connection . SetShouldDisconnect ( ) ;
}
idAndSizePtr [ 0 ] = info . messageId > > 8 ;
* ( u32 * ) ( idAndSizePtr + 1 ) = bodySize | u32 ( info . messageId < < 24 ) ;
2024-03-03 21:30:10 -05:00
connection . Send ( writer . GetData ( ) , u32 ( writer . GetPosition ( ) ) ) ;
return true ;
}
2023-11-29 18:47:11 -05:00
NetworkServer : : Worker * NetworkServer : : PopWorker ( )
{
2024-02-19 02:01:21 -05:00
while ( true )
{
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_availableWorkersLock , lock ) ;
2024-02-19 02:01:21 -05:00
if ( ! m_workersEnabled )
return nullptr ;
if ( auto worker = PopWorkerNoLock ( ) )
return worker ;
m_workerAvailable . Reset ( ) ;
lock . Leave ( ) ;
m_workerAvailable . IsSet ( ) ;
}
2023-11-29 18:47:11 -05:00
}
NetworkServer : : Worker * NetworkServer : : PopWorkerNoLock ( )
{
Worker * worker = m_firstAvailableWorker ;
if ( worker )
{
m_firstAvailableWorker = worker - > m_nextWorker ;
if ( m_firstAvailableWorker )
m_firstAvailableWorker - > m_prevWorker = nullptr ;
}
else
{
2024-02-19 02:01:21 -05:00
if ( m_createdWorkerCount = = m_maxWorkerCount )
return nullptr ;
2023-11-29 18:47:11 -05:00
worker = new Worker ( ) ;
worker - > Start ( * this ) ;
+ + m_createdWorkerCount ;
}
if ( m_firstActiveWorker )
m_firstActiveWorker - > m_prevWorker = worker ;
worker - > m_nextWorker = m_firstActiveWorker ;
m_firstActiveWorker = worker ;
2024-02-19 02:01:21 -05:00
worker - > m_inUse = true ;
2023-11-29 18:47:11 -05:00
return worker ;
}
void NetworkServer : : PushWorker ( Worker * worker )
{
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_availableWorkersLock , lock ) ;
2023-11-29 18:47:11 -05:00
PushWorkerNoLock ( worker ) ;
}
void NetworkServer : : PushWorkerNoLock ( Worker * worker )
{
2024-02-19 02:01:21 -05:00
UBA_ASSERT ( worker - > m_inUse ) ;
2023-11-29 18:47:11 -05:00
if ( worker - > m_prevWorker )
worker - > m_prevWorker - > m_nextWorker = worker - > m_nextWorker ;
else
m_firstActiveWorker = worker - > m_nextWorker ;
if ( worker - > m_nextWorker )
worker - > m_nextWorker - > m_prevWorker = worker - > m_prevWorker ;
if ( m_firstAvailableWorker )
m_firstAvailableWorker - > m_prevWorker = worker ;
worker - > m_prevWorker = nullptr ;
worker - > m_nextWorker = m_firstAvailableWorker ;
2024-02-19 02:01:21 -05:00
worker - > m_inUse = false ;
2023-11-29 18:47:11 -05:00
m_firstAvailableWorker = worker ;
m_workerAvailable . Set ( ) ;
}
2024-03-08 18:31:48 -05:00
void NetworkServer : : FlushWorkers ( )
{
SCOPED_WRITE_LOCK ( m_availableWorkersLock , lock ) ;
while ( auto worker = m_firstActiveWorker )
{
lock . Leave ( ) ;
worker - > Stop ( * this ) ;
lock . Enter ( ) ;
}
auto worker = m_firstAvailableWorker ;
while ( worker )
{
auto temp = worker ;
worker = worker - > m_nextWorker ;
delete temp ;
}
m_firstAvailableWorker = nullptr ;
2024-06-07 16:29:55 -04:00
m_maxCreatedWorkerCount = Max ( m_createdWorkerCount , m_maxCreatedWorkerCount ) ;
m_createdWorkerCount = 0 ;
2024-03-08 18:31:48 -05:00
}
2023-11-29 18:47:11 -05:00
void NetworkServer : : RemoveDisconnectedConnections ( )
{
for ( auto it = m_connections . begin ( ) ; it ! = m_connections . end ( ) ; )
{
Connection & con = * it ;
if ( ! con . m_disconnected )
{
+ + it ;
continue ;
}
2024-06-02 18:14:50 -04:00
m_sendTimer + = con . m_sendTimer ;
2023-11-29 18:47:11 -05:00
it = m_connections . erase ( it ) ;
}
}
bool NetworkServer : : HandleSystemMessage ( const ConnectionInfo & connectionInfo , u8 messageType , BinaryReader & reader , BinaryWriter & writer )
{
switch ( messageType )
{
case SystemMessageType_SetConnectionCount :
{
u32 connectionCount = reader . ReadU32 ( ) ;
2024-02-26 01:14:42 -05:00
SCOPED_READ_LOCK ( m_clientsLock , lock ) ;
2023-11-29 18:47:11 -05:00
auto findIt = m_clients . find ( connectionInfo . GetId ( ) ) ;
if ( findIt = = m_clients . end ( ) )
return true ;
Client & c = findIt - > second ;
lock . Leave ( ) ;
if ( c . connectionCount > = connectionCount )
return true ;
u32 toAdd = connectionCount - c . connectionCount ;
2023-12-14 02:22:56 -05:00
auto connPtr = ( NetworkServer : : Connection * ) connectionInfo . internalData ;
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_addConnectionsLock , lock2 ) ;
2023-11-29 18:47:11 -05:00
for ( u32 i = 0 ; i ! = toAdd ; + + i )
{
2023-12-14 02:22:56 -05:00
m_addConnections . emplace_back ( [ this , connPtr ] ( )
2023-11-29 18:47:11 -05:00
{
2023-12-14 02:22:56 -05:00
auto & conn = * connPtr ;
conn . m_backend . Connect ( m_logger , conn . m_remoteSockAddr , [ this , & conn ] ( void * connection , const sockaddr & remoteSocketAddr , bool * timedOut )
2023-11-29 18:47:11 -05:00
{
CryptoKey cryptoKey = InvalidCryptoKey ;
if ( conn . m_cryptoKey )
{
cryptoKey = Crypto : : DuplicateKey ( m_logger , conn . m_cryptoKey ) ;
if ( cryptoKey = = InvalidCryptoKey )
return false ;
}
2024-06-10 23:37:00 -04:00
return AddConnection ( conn . m_backend , connection , remoteSocketAddr , cryptoKey ! = InvalidCryptoKey , cryptoKey ) ;
2023-11-29 18:47:11 -05:00
} , nullptr ) ;
return 0 ;
} ) ;
}
return true ;
}
case SystemMessageType_KeepAlive :
{
// No-op
return true ;
}
}
return false ;
}
2024-06-10 23:37:00 -04:00
bool NetworkServer : : AddConnection ( NetworkBackend & backend , void * backendConnection , const sockaddr & remoteSocketAddr , bool requiresCrypto , CryptoKey cryptoKey )
2023-11-29 18:47:11 -05:00
{
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_connectionsLock , lock ) ;
2023-11-29 18:47:11 -05:00
RemoveDisconnectedConnections ( ) ;
2024-03-08 18:31:48 -05:00
if ( ! m_workersEnabled )
{
// Just to prevent errors in log
backend . SetDisconnectCallback ( backendConnection , nullptr , [ ] ( void * , const Guid & , void * ) { } ) ;
backend . SetRecvCallbacks ( backendConnection , nullptr , 0 , [ ] ( void * , const Guid & , u8 * , void * & , u8 * & , u32 & ) { return false ; } , nullptr , TC ( " Disconnecting " ) ) ;
return false ;
}
2024-06-10 23:37:00 -04:00
m_connections . emplace_back ( * this , backend , backendConnection , remoteSocketAddr , requiresCrypto , cryptoKey , m_connectionIdCounter + + ) ;
2023-11-29 18:47:11 -05:00
m_maxActiveConnections = Max ( m_maxActiveConnections , u32 ( m_connections . size ( ) ) ) ;
return true ;
}
}