polserver/pol-core/pol/network/client.cpp
Lukáš Novotný d6bde75dec
Add proxy protocol support (#623)
* Add proxy protocol support

* style fix

* more style changes

* hopefully final style fixes

* Implement AllowProxy instead of UseProxyProtocol

* Style fix

* Fix initialization warning; remove unused header

* Fix boost warning (thanks @KevinEady)
2024-02-21 21:41:17 +01:00

761 lines
22 KiB
C++

/** @file
*
* @par History
* - 2005/01/24 Shinigami: added getspyonclient2 to support packet 0xd9 (Spy on Client 2)
* - 2005/08/29 Shinigami: character.spyonclient2 renamed to character.clientinfo
* getspyonclient2 renamed to getclientinfo
* - 2007/07/09 Shinigami: added isUOKR [bool] - UO:KR client used?
* - 2009/07/20 MuadDib: Added statement to bypass cryptseed at login. Handled by changing default
* client recv_state using ssopt flag.
* - 2009/07/23 MuadDib: updates for new Enum::Packet Out ID
* - 2009/08/25 Shinigami: STLport-5.2.1 fix: init order changed of aosresist
* STLport-5.2.1 fix: params in call of Log2()
* - 2009/09/06 Turley: Added u8 ClientType + FlagEnum
* Removed is*
* - 2010/01/22 Turley: Speedhack Prevention System
*/
#include "client.h"
#include <errno.h>
#include <stdlib.h>
#include <time.h>
#include "../../bscript/berror.h"
#include "../../bscript/bstruct.h"
#include "../../bscript/impstr.h"
#include "../../clib/clib.h"
#include "../../clib/logfacility.h"
#include "../../clib/strutil.h" //CNXBUG
#include "../../clib/wallclock.h"
#include "../accounts/account.h"
#include "../crypt/cryptbase.h"
#include "../crypt/cryptengine.h"
#include "../globals/network.h"
#include "../globals/state.h"
#include "../globals/uvars.h"
#include "../mobile/charactr.h"
#include "../polsig.h"
#include "../realms/WorldChangeReasons.h"
#include "../ufunc.h" // only in here temporarily, until logout-on-disconnect stuff is removed
#include "../uoclient.h"
#include "../uoscrobj.h"
#include "../uworld.h"
#include "cgdata.h"
#include "cliface.h"
#include "pktdef.h"
#include "pktin.h"
#include "xbuffer.h"
#define PRE_ENCRYPT
#ifndef PRE_ENCRYPT
#include "sockio.h"
#endif
#ifdef _MSC_VER
#pragma warning( \
disable : 4351 ) // new behavior: elements of array '...' will be default initialized
#endif
namespace Pol
{
namespace Core
{
void cancel_trade( Mobile::Character* chr1 );
}
namespace Network
{
unsigned int Client::instance_counter_;
ThreadedClient::ThreadedClient( Crypt::TCryptInfo& encryption, Client& myClient,
sockaddr& client_addr,
std::vector<boost::asio::ip::network_v4>& allowed_proxies )
: myClient( myClient ),
thread_pid( static_cast<size_t>( -1 ) ),
csocket( INVALID_SOCKET ),
preDisconnect( false ),
disconnect( false ),
cryptengine( create_crypt_engine( encryption ) ),
encrypt_server_stream( false ),
allowed_proxies( allowed_proxies ),
last_activity_at( 0 ),
last_packet_at( 0 ),
recv_state( RECV_STATE_CRYPTSEED_WAIT ),
bufcheck1_AA( 0xAA ),
buffer(), // zero-initializes the buffer
bufcheck2_55( 0x55 ),
bytes_received( 0 ),
message_length( 0 ),
last_msgtype( 255 ),
msgtype_filter( Core::networkManager.login_filter.get() ),
checkpoint( -1 ), // CNXBUG
_fpLog_lock(),
fpLog( "" ),
disable_inactivity_timeout( false ),
first_xmit_buffer( nullptr ),
last_xmit_buffer( nullptr ),
n_queued( 0 ),
queued_bytes_counter( 0 )
{
memset( &counters, 0, sizeof counters );
memcpy( &ipaddr, &client_addr, sizeof ipaddr );
memset( &ipaddr_proxy, 0, sizeof( ipaddr_proxy ) );
if ( ipaddr.sa_family == AF_INET )
{
// accept proxy protocol only from allowed ips
auto ipaddrv4 = reinterpret_cast<sockaddr_in*>( &ipaddr );
auto my_address =
#ifdef _WIN32
boost::asio::ip::address_v4( htonl(ipaddrv4->sin_addr.S_un.S_addr ) );
#else
boost::asio::ip::address_v4( htonl(ipaddrv4->sin_addr.s_addr ) );
#endif
auto my_network = boost::asio::ip::network_v4( my_address, 32 );
for ( const auto& allowed_proxy : allowed_proxies )
{
if ( my_network == allowed_proxy || my_network.is_subnet_of( allowed_proxy ) )
{
recv_state = RECV_STATE_PROXYPROTOCOLHEADER_WAIT;
break;
}
}
}
};
Client::Client( ClientInterface& aInterface, Crypt::TCryptInfo& encryption, sockaddr& ipaddr,
std::vector<boost::asio::ip::network_v4>& allowed_proxies )
: ThreadedClient( encryption, *this, ipaddr, allowed_proxies ),
acct( nullptr ),
chr( nullptr ),
Interface( aInterface ),
ready( false ),
listen_port( 0 ),
aosresist( false ),
pause_count( 0 ),
gd( new ClientGameData ),
instance_( ++instance_counter_ ),
UOExpansionFlag( 0 ),
UOExpansionFlagClient( 0 ),
ClientType( 0 ),
next_movement( 0 ),
movementsequence( 0 ),
paused_( false )
{
weakptr.set( this ); // store weakptr for usage in scripts (see EClientRefObjImp)
// For bypassing cryptseed packet
if ( Core::settingsManager.ssopt.use_edit_server )
{
recv_state = RECV_STATE_MSGTYPE_WAIT;
}
Interface.register_client( this );
memset( &clientinfo_, 0, sizeof( clientinfo_ ) );
memset( &versiondetail_, 0, sizeof( versiondetail_ ) );
}
Client::~Client()
{
PreDelete();
delete cryptengine;
}
void Client::init_crypto( void* nseed, int type )
{
session()->cryptengine->Init( nseed, type );
}
void Client::unregister()
{
auto findClient =
std::find( Core::networkManager.clients.begin(), Core::networkManager.clients.end(), this );
Core::networkManager.clients.erase( findClient ); // TODO: Make networkManager more OO
Interface.deregister_client( this );
}
void Client::PreDelete()
{
closeConnection();
if ( chr != nullptr && chr->client == this )
{
if ( chr->logged_in() )
{
ClrCharacterWorldPosition( chr, Realms::WorldChangeReason::PlayerExit );
send_remove_character_to_nearby( chr );
chr->logged_in( false );
chr->set_opponent( nullptr );
chr->removal_cleanup();
if ( chr->get_opponent() != nullptr )
{
chr->set_opponent( nullptr, true );
}
}
else
{
ERROR_PRINTLN( "Uhh... active character not logged in!??" );
}
}
// detach the account and character from this client, if they
// are still associated with it.
acct = nullptr;
if ( chr )
{
if ( chr->client == this )
chr->client = nullptr;
chr = nullptr;
}
// stop packet-logging
stop_log();
delete gd;
gd = nullptr;
while ( first_xmit_buffer != nullptr )
{
Core::XmitBuffer* xbuffer = first_xmit_buffer;
first_xmit_buffer = first_xmit_buffer->next;
free( xbuffer );
--n_queued;
}
last_xmit_buffer = nullptr;
// while (!movementqueue.empty())
// movementqueue.pop();
}
// ClientInfo - delivers a lot of usefull infomation about client PC
Bscript::BStruct* Client::getclientinfo() const
{
using namespace Bscript;
std::unique_ptr<BStruct> ret( new BStruct );
ret->addMember( "unknown1", new BLong( clientinfo_.unknown1 ) ); // Unknown - allways 0x02
ret->addMember( "instance", new BLong( clientinfo_.instance ) ); // Unique Instance ID of UO
ret->addMember( "os_major", new BLong( clientinfo_.os_major ) ); // OS Major
ret->addMember( "os_minor", new BLong( clientinfo_.os_minor ) ); // OS Minor
ret->addMember( "os_revision", new BLong( clientinfo_.os_revision ) ); // OS Revision
ret->addMember( "cpu_manufacturer",
new BLong( clientinfo_.cpu_manufacturer ) ); // CPU Manufacturer
ret->addMember( "cpu_family", new BLong( clientinfo_.cpu_family ) ); // CPU Family
ret->addMember( "cpu_model", new BLong( clientinfo_.cpu_model ) ); // CPU Model
ret->addMember( "cpu_clockspeed",
new BLong( clientinfo_.cpu_clockspeed ) ); // CPU Clock Speed [Mhz]
ret->addMember( "cpu_quantity", new BLong( clientinfo_.cpu_quantity ) ); // CPU Quantity
ret->addMember( "memory", new BLong( clientinfo_.memory ) ); // Memory [MB]
ret->addMember( "screen_width", new BLong( clientinfo_.screen_width ) ); // Screen Width
ret->addMember( "screen_height", new BLong( clientinfo_.screen_height ) ); // Screen Height
ret->addMember( "screen_depth", new BLong( clientinfo_.screen_depth ) ); // Screen Depth [Bit]
ret->addMember( "directx_major", new BLong( clientinfo_.directx_major ) ); // DirectX Major
ret->addMember( "directx_minor", new BLong( clientinfo_.directx_minor ) ); // DirectX Minor
unsigned maxlen_vd =
sizeof( clientinfo_.video_description ) / sizeof( clientinfo_.video_description[0] );
std::string vd = Bscript::String::fromUTF16( &clientinfo_.video_description[0], maxlen_vd, true );
ret->addMember( "video_description",
new Bscript::String( vd ) ); // Video Card Description
ret->addMember( "video_vendor", new BLong( clientinfo_.video_vendor ) ); // Video Card Vendor ID
ret->addMember( "video_device", new BLong( clientinfo_.video_device ) ); // Video Card Device ID
ret->addMember( "video_memory",
new BLong( clientinfo_.video_memory ) ); // Video Card Memory [MB]
ret->addMember( "distribution", new BLong( clientinfo_.distribution ) ); // Distribution
ret->addMember( "clients_running", new BLong( clientinfo_.clients_running ) ); // Clients Running
ret->addMember( "clients_installed",
new BLong( clientinfo_.clients_installed ) ); // Clients Installed
ret->addMember( "partial_installed",
new BLong( clientinfo_.partial_installed ) ); // Partial Insstalled
unsigned maxlen_lc = sizeof( clientinfo_.langcode ) / sizeof( clientinfo_.langcode[0] );
std::string lc = Bscript::String::fromUTF16( &clientinfo_.langcode[0], maxlen_lc, true );
ret->addMember( "langcode",
new Bscript::String( lc ) ); // Language Code
std::unique_ptr<ObjArray> arr_u2( new ObjArray );
for ( unsigned i = 0; i < sizeof( clientinfo_.unknown2 ); ++i )
arr_u2->addElement( new BLong( clientinfo_.unknown2[i] ) );
ret->addMember( "unknown2", arr_u2.release() ); // Unknown
return ret.release();
}
void Client::itemizeclientversion( const std::string& ver, VersionDetailStruct& detail )
{
try
{
size_t dot1 = ver.find_first_of( '.', 0 );
size_t dot2 = ver.find_first_of( '.', dot1 + 1 );
size_t dot3 = ver.find_first_of( '.', dot2 + 1 );
if ( dot3 == std::string::npos ) // since 5.0.7 patch is digit
{
dot3 = dot2 + 1;
while ( ( dot3 < ver.length() ) && ( isdigit( ver[dot3] ) ) )
{
dot3++;
}
}
detail.major = atoi( ver.substr( 0, dot1 ).c_str() );
detail.minor = atoi( ver.substr( dot1 + 1, dot2 - dot1 - 1 ).c_str() );
detail.rev = atoi( ver.substr( dot2 + 1, dot3 - dot2 - 1 ).c_str() );
detail.patch = 0;
if ( dot3 < ver.length() )
{
if ( ( detail.major <= 5 ) && ( detail.minor <= 0 ) && ( detail.rev <= 6 ) )
{
if ( ver[dot3] != ' ' )
detail.patch = ( ver[dot3] - 'a' ) + 1; // char to int
}
else
detail.patch = atoi( ver.substr( dot3 + 1, ver.length() - dot3 - 1 ).c_str() );
}
}
catch ( ... )
{
detail.major = 0;
detail.minor = 0;
detail.rev = 0;
detail.patch = 0;
POLLOGLN( "Malformed clientversion string: {}", ver );
}
}
bool Client::compareVersion( const std::string& ver )
{
VersionDetailStruct ver2;
itemizeclientversion( ver, ver2 );
return Client::compareVersion( ver2 );
}
bool Client::compareVersion( const VersionDetailStruct& ver2 )
{
VersionDetailStruct ver1 = getversiondetail();
if ( ver1.major > ver2.major )
return true;
else if ( ver1.major < ver2.major )
return false;
else if ( ver1.minor > ver2.minor )
return true;
else if ( ver1.minor < ver2.minor )
return false;
else if ( ver1.rev > ver2.rev )
return true;
else if ( ver1.rev < ver2.rev )
return false;
else if ( ver1.patch > ver2.patch )
return true;
else if ( ver1.patch < ver2.patch )
return false;
else
return true;
}
void Client::setClientType( ClientTypeFlag type )
{
ClientType = 0x0;
// with fall through !
switch ( type )
{
case CLIENTTYPE_70331:
ClientType |= CLIENTTYPE_70331;
// fall through
case CLIENTTYPE_70300:
ClientType |= CLIENTTYPE_70300;
// fall through
case CLIENTTYPE_70130:
ClientType |= CLIENTTYPE_70130;
// fall through
case CLIENTTYPE_7090:
ClientType |= CLIENTTYPE_7090;
// fall through
case CLIENTTYPE_UOSA:
ClientType |= CLIENTTYPE_UOSA;
// fall through
case CLIENTTYPE_7000:
ClientType |= CLIENTTYPE_7000;
// fall through
case CLIENTTYPE_UOKR:
ClientType |= CLIENTTYPE_UOKR;
// fall through
case CLIENTTYPE_60142:
ClientType |= CLIENTTYPE_60142;
// fall through
case CLIENTTYPE_6017:
ClientType |= CLIENTTYPE_6017;
// fall through
case CLIENTTYPE_5020:
ClientType |= CLIENTTYPE_5020;
// fall through
case CLIENTTYPE_5000:
ClientType |= CLIENTTYPE_5000;
// fall through
case CLIENTTYPE_4070:
ClientType |= CLIENTTYPE_4070;
// fall through
case CLIENTTYPE_4000:
ClientType |= CLIENTTYPE_4000;
// fall through
default:
break;
}
}
bool Client::IsUOKRClient()
{
return ( ( ClientType & CLIENTTYPE_UOKR ) && ( !( ClientType & CLIENTTYPE_7000 ) ) );
}
std::string Client::status() const
{
std::string st;
if ( acct != nullptr )
st += "AC:" + std::string( acct->name() ) + " ";
if ( chr != nullptr )
st += "CH:" + chr->name() + " ";
if ( have_queued_data() )
st += "TXBUF ";
if ( disconnect )
st += "DISC ";
if ( paused_ )
st += "PAUSE ";
if ( ready )
st += "RDY ";
st += ipaddrAsString() + " ";
st += "CHK: " + Clib::tostring( checkpoint ) + " ";
st += "PID: " + Clib::tostring( thread_pid ) + " ";
st += "LAST: " + Clib::hexint( last_msgtype );
return st;
}
void ThreadedClient::queue_data( const void* data, unsigned short datalen )
{
THREAD_CHECKPOINT( active_client, 300 );
Core::XmitBuffer* xbuffer = (Core::XmitBuffer*)malloc( sizeof( Core::XmitBuffer ) - 1 + datalen );
THREAD_CHECKPOINT( active_client, 301 );
if ( xbuffer )
{
THREAD_CHECKPOINT( active_client, 302 );
xbuffer->next = nullptr;
xbuffer->nsent = 0;
xbuffer->lenleft = datalen;
memcpy( xbuffer->data, data, datalen );
THREAD_CHECKPOINT( active_client, 303 );
if ( first_xmit_buffer == nullptr || last_xmit_buffer == nullptr )
{ // in this case, last_xmit_buffer is also nullptr, so can't set its ->next.
THREAD_CHECKPOINT( active_client, 304 );
first_xmit_buffer = xbuffer;
}
else
{
THREAD_CHECKPOINT( active_client, 305 );
last_xmit_buffer->next = xbuffer;
}
THREAD_CHECKPOINT( active_client, 306 );
last_xmit_buffer = xbuffer;
++n_queued;
queued_bytes_counter += datalen;
}
else
{
THREAD_CHECKPOINT( active_client, 307 );
POLLOGLN( "Client#{}: Unable to allocate {} bytes for queued data. Disconnecting.",
myClient.instance_, ( sizeof( Core::XmitBuffer ) - 1 + datalen ) );
disconnect = true;
}
THREAD_CHECKPOINT( active_client, 309 );
}
void ThreadedClient::xmit( const void* data, unsigned short datalen )
{
if ( csocket == INVALID_SOCKET )
return;
if ( encrypt_server_stream )
{
if ( cryptengine == nullptr )
return;
this->cryptengine->Encrypt( (void*)data, (void*)data, datalen );
}
THREAD_CHECKPOINT( active_client, 200 );
if ( last_xmit_buffer ) // this client already backlogged, schedule for later
{
THREAD_CHECKPOINT( active_client, 201 );
queue_data( data, datalen );
THREAD_CHECKPOINT( active_client, 202 );
return;
}
THREAD_CHECKPOINT( active_client, 203 );
/* client not backlogged - try to send. */
const unsigned char* cdata = (const unsigned char*)data;
int nsent;
if ( -1 == ( nsent = send( csocket, (const char*)cdata, datalen, 0 ) ) )
{
THREAD_CHECKPOINT( active_client, 204 );
int sckerr = socket_errno;
if ( sckerr == SOCKET_ERRNO( EWOULDBLOCK ) )
{
THREAD_CHECKPOINT( active_client, 205 );
POLLOG_ERRORLN( "Client#{}: Switching to queued data mode (1, {} bytes)", myClient.instance_,
datalen );
THREAD_CHECKPOINT( active_client, 206 );
queue_data( data, datalen );
THREAD_CHECKPOINT( active_client, 207 );
return;
}
else
{
THREAD_CHECKPOINT( active_client, 208 );
if ( !disconnect )
POLLOG_ERRORLN( "Client#{}: Disconnecting client due to send() error (1): {}",
myClient.instance_, sckerr );
disconnect = true;
THREAD_CHECKPOINT( active_client, 209 );
return;
}
}
else // no error
{
THREAD_CHECKPOINT( active_client, 210 );
datalen -= static_cast<unsigned short>( nsent );
counters.bytes_transmitted += nsent;
Core::networkManager.polstats.bytes_sent += nsent;
if ( datalen ) // anything left? if so, queue for later.
{
THREAD_CHECKPOINT( active_client, 211 );
POLLOG_ERRORLN( "Client#{}: Switching to queued data mode (2)", myClient.instance_ );
THREAD_CHECKPOINT( active_client, 212 );
queue_data( cdata + nsent, datalen );
THREAD_CHECKPOINT( active_client, 213 );
}
}
THREAD_CHECKPOINT( active_client, 214 );
}
void ThreadedClient::send_queued_data()
{
std::lock_guard<std::mutex> lock( _socketMutex );
Core::XmitBuffer* xbuffer;
// hand off data to the sockets layer until it won't take any more.
// note if a buffer is sent in full, we try to send the next one, ad infinitum
while ( nullptr != ( xbuffer = first_xmit_buffer ) )
{
int nsent;
nsent = send( csocket, (char*)&xbuffer->data[xbuffer->nsent], xbuffer->lenleft, 0 );
if ( nsent == -1 )
{
#ifdef _WIN32
int sckerr = WSAGetLastError();
#else
int sckerr = errno;
#endif
if ( sckerr == SOCKET_ERRNO( EWOULDBLOCK ) )
{
// do nothing. it'll be re-queued later, when it won't block.
return;
}
else
{
if ( !disconnect )
POLLOGLN( "Client#{}: Disconnecting client due to send() error (2): {}",
myClient.instance_, sckerr );
disconnect = true;
return;
}
}
else
{
xbuffer->nsent += static_cast<unsigned short>( nsent );
xbuffer->lenleft -= static_cast<unsigned short>( nsent );
counters.bytes_transmitted += nsent;
Core::networkManager.polstats.bytes_sent += nsent;
if ( xbuffer->lenleft == 0 )
{
first_xmit_buffer = first_xmit_buffer->next;
if ( first_xmit_buffer == nullptr )
{
last_xmit_buffer = nullptr;
POLLOGLN( "Client#{}: Leaving queued mode ({} bytes xmitted)", myClient.instance_,
queued_bytes_counter );
queued_bytes_counter = 0;
}
free( xbuffer );
--n_queued;
}
}
}
}
// 33 01 "encrypted": 4F FA
static const unsigned char pause_pre_encrypted[2] = { 0x4F, 0xFA };
// 33 00 "encrypted": 4C D0
static const unsigned char restart_pre_encrypted[2] = { 0x4C, 0xD0 };
void Client::send_pause()
{
if ( Core::networkManager.uoclient_protocol.EnableFlowControlPackets && !paused_ )
{
#ifndef PRE_ENCRYPT
PKTOUT_33 msg;
msg.msgtype = PKTOUT_33_ID;
msg.flow = MSGOPT_33_FLOW_PAUSE;
transmit( this, &msg, sizeof msg );
#else
xmit( pause_pre_encrypted, sizeof pause_pre_encrypted );
#endif
paused_ = true;
}
}
void Client::pause()
{
if ( !pause_count )
{
send_pause();
pause_count = 1;
}
}
void Client::send_restart()
{
if ( paused_ )
{
#ifndef PRE_ENCRYPT
PKTOUT_33 msg;
msg.msgtype = PKTOUT_33_ID;
msg.flow = MSGOPT_33_FLOW_RESTART;
transmit( this, &msg, sizeof msg );
#else
xmit( restart_pre_encrypted, sizeof restart_pre_encrypted );
#endif
paused_ = false;
}
}
void Client::restart()
{
send_restart();
pause_count = 0;
}
// Note: this doesnt test single packets it only summs the delay and tests
// here only the "start"-value is set the additional delay is set in PKT_02 handler
bool Client::SpeedHackPrevention( bool add )
{
if ( ( !movementqueue.empty() ) && ( add ) )
{
if ( movementqueue.size() > 100 )
{
POLLOG_ERRORLN( "Client#{}: More then 100 Movepackets in queue. Disconnecting.", instance_ );
disconnect = true;
return false;
}
PacketThrottler throttlestruct;
memcpy( &throttlestruct.pktbuffer, &buffer, PKTIN_02_SIZE );
movementqueue.push( throttlestruct );
return false;
}
if ( chr != nullptr && chr->can_speedhack() )
return true;
if ( ( next_movement == 0 ) ||
( Clib::wallclock() > next_movement ) ) // never moved or in the past
{
next_movement = Clib::wallclock();
return true;
}
// now we dont alter next_movement so we can sum the delay till diff is greater then error margin
Clib::wallclock_diff_t diff = Clib::wallclock_diff_ms( Clib::wallclock(), next_movement );
if ( diff > PKTIN_02_ASYNCHRONOUS ) // delay sum greater then our error margin?
{
if ( add ) // delay packet
{
if ( movementqueue.size() > 100 )
{
POLLOG_ERRORLN( "Client#{}: More then 100 Movepackets in queue. Disconnecting.",
instance_ );
disconnect = true;
return false;
}
PacketThrottler throttlestruct;
memcpy( &throttlestruct.pktbuffer, &buffer, sizeof( throttlestruct.pktbuffer ) );
movementqueue.push( throttlestruct );
}
return false;
}
return true;
}
Bscript::BObjectImp* Client::make_ref()
{
return new Module::EClientRefObjImp( weakptr );
}
weak_ptr<Client> Client::getWeakPtr() const
{
return weakptr;
}
void Client::set_update_range( u8 range )
{
// store "personal" updaterange
gd->update_range = range;
// update global updaterange (maximum multi radius/client view range)
Core::gamestate.update_range_from_client( range );
}
u8 Client::update_range() const
{
return gd->update_range;
}
// TODO: Add estimatedSize() to ThreadedClient and move the corresponding members
size_t Client::estimatedSize() const
{
Clib::SpinLockGuard guard( _fpLog_lock );
size_t size = sizeof( Client ) + fpLog.capacity() + version_.capacity();
Core::XmitBuffer* buffer_size = first_xmit_buffer;
while ( buffer_size != nullptr )
{
size += sizeof( buffer_size ) + buffer_size->lenleft;
buffer_size = buffer_size->next;
}
size += 3 * sizeof( PacketThrottler* ) + movementqueue.size() * sizeof( PacketThrottler );
if ( gd != nullptr )
size += gd->estimatedSize();
return size;
}
// Threaded client stuff
void ThreadedClient::closeConnection()
{
std::lock_guard<std::mutex> lock( _socketMutex );
if ( csocket != INVALID_SOCKET )
{
#ifdef _WIN32
shutdown( csocket, 2 ); // 2 is both sides, defined in winsock2.h ...
closesocket( csocket );
#else
shutdown( csocket, SHUT_RDWR );
close( csocket );
#endif
}
csocket = INVALID_SOCKET;
}
} // namespace Network
} // namespace Pol