• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

polserver / polserver / 25972182398

16 May 2026 08:30PM UTC coverage: 60.836% (-0.07%) from 60.903%
25972182398

Pull #884

github

turleypol
updated dynproperties
Pull Request #884: Attackable item

241 of 527 new or added lines in 26 files covered. (45.73%)

17 existing lines in 7 files now uncovered.

44729 of 73524 relevant lines covered (60.84%)

446850.64 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

47.16
/pol-core/pol/network/client.cpp
1
/** @file
2
 *
3
 * @par History
4
 * - 2005/01/24 Shinigami: added getspyonclient2 to support packet 0xd9 (Spy on Client 2)
5
 * - 2005/08/29 Shinigami: character.spyonclient2 renamed to character.clientinfo
6
 *                         getspyonclient2 renamed to getclientinfo
7
 * - 2007/07/09 Shinigami: added isUOKR [bool] - UO:KR client used?
8
 * - 2009/07/20 MuadDib:   Added statement to bypass cryptseed at login. Handled by changing default
9
 * client recv_state using ssopt flag.
10
 * - 2009/07/23 MuadDib:   updates for new Enum::Packet Out ID
11
 * - 2009/08/25 Shinigami: STLport-5.2.1 fix: init order changed of aosresist
12
 *                         STLport-5.2.1 fix: params in call of Log2()
13
 * - 2009/09/06 Turley:    Added u8 ClientType + FlagEnum
14
 *                         Removed is*
15
 * - 2010/01/22 Turley:    Speedhack Prevention System
16
 */
17

18

19
#include "client.h"
20

21
#include <errno.h>
22
#include <stdlib.h>
23
#include <time.h>
24

25
#include "../../bscript/berror.h"
26
#include "../../bscript/bstruct.h"
27
#include "../../bscript/impstr.h"
28
#include "../../clib/clib.h"
29
#include "../../clib/logfacility.h"
30
#include "../../clib/stlutil.h"
31
#include "../../clib/strutil.h"  //CNXBUG
32
#include "../../clib/wallclock.h"
33
#include "../accounts/account.h"
34
#include "../crypt/cryptbase.h"
35
#include "../crypt/cryptengine.h"
36
#include "../globals/network.h"
37
#include "../globals/settings.h"
38
#include "../globals/state.h"
39
#include "../globals/uvars.h"
40
#include "../mobile/charactr.h"
41
#include "../module/uomod.h"
42
#include "../polsig.h"
43
#include "../realms/WorldChangeReasons.h"
44
#include "../ufunc.h"  // only in here temporarily, until logout-on-disconnect stuff is removed
45
#include "../uoclient.h"
46
#include "../uoscrobj.h"
47
#include "../uworld.h"
48
#include "cgdata.h"
49
#include "cliface.h"
50
#include "packethelper.h"
51
#include "packets.h"
52
#include "pktdef.h"
53
#include "pktin.h"
54
#include "xbuffer.h"
55

56

57
#define PRE_ENCRYPT
58

59
#ifndef PRE_ENCRYPT
60
#include "sockio.h"
61
#endif
62

63
#ifdef _MSC_VER
64
#pragma warning( disable \
65
                 : 4351 )  // new behavior: elements of array '...' will be default initialized
66
#endif
67

68
namespace Pol
69
{
70
namespace Core
71
{
72
void cancel_trade( Mobile::Character* chr1 );
73
}
74
namespace Network
75
{
76
unsigned int Client::instance_counter_;
77

78
ThreadedClient::ThreadedClient( Crypt::TCryptInfo& encryption, Client& myClient,
4✔
79
                                sockaddr& client_addr,
80
                                std::vector<boost::asio::ip::network_v4>& allowed_proxies )
4✔
81
    : myClient( myClient ),
4✔
82
      thread_pid( static_cast<size_t>( -1 ) ),
4✔
83
      csocket( INVALID_SOCKET ),
4✔
84
      preDisconnect( false ),
4✔
85
      disconnect( false ),
4✔
86
      cryptengine( create_crypt_engine( encryption ) ),
4✔
87
      encrypt_server_stream( false ),
4✔
88
      allowed_proxies( allowed_proxies ),
4✔
89
      last_activity_at( 0 ),
4✔
90
      last_packet_at( 0 ),
4✔
91
      recv_state( RECV_STATE_CRYPTSEED_WAIT ),
4✔
92
      bufcheck1_AA( 0xAA ),
4✔
93
      buffer(),  // zero-initializes the buffer
10,244✔
94
      bufcheck2_55( 0x55 ),
4✔
95
      bytes_received( 0 ),
4✔
96
      message_length( 0 ),
4✔
97
      last_msgtype( 255 ),
4✔
98
      msgtype_filter( Core::networkManager.login_filter.get() ),
4✔
99
      checkpoint( -1 ),  // CNXBUG
4✔
100
      _fpLog_lock(),
4✔
101
      fpLog( "" ),
4✔
102
      disable_inactivity_timeout( false ),
4✔
103
      first_xmit_buffer( nullptr ),
4✔
104
      last_xmit_buffer( nullptr ),
4✔
105
      n_queued( 0 ),
4✔
106
      queued_bytes_counter( 0 )
8✔
107
{
108
  memset( &counters, 0, sizeof counters );
4✔
109
  memcpy( &ipaddr, &client_addr, sizeof ipaddr );
4✔
110
  memset( &ipaddr_proxy, 0, sizeof( ipaddr_proxy ) );
4✔
111

112
  if ( ipaddr.sa_family == AF_INET )
4✔
113
  {
114
    // accept proxy protocol only from allowed ips
115
    auto ipaddrv4 = reinterpret_cast<sockaddr_in*>( &ipaddr );
4✔
116
    auto my_address =
117
#ifdef _WIN32
118
        boost::asio::ip::address_v4( htonl( ipaddrv4->sin_addr.S_un.S_addr ) );
119
#else
120
        boost::asio::ip::address_v4( htonl( ipaddrv4->sin_addr.s_addr ) );
4✔
121
#endif
122
    auto my_network = boost::asio::ip::network_v4( my_address, 32 );
4✔
123
    for ( const auto& allowed_proxy : allowed_proxies )
4✔
124
    {
125
      if ( my_network == allowed_proxy || my_network.is_subnet_of( allowed_proxy ) )
×
126
      {
127
        recv_state = RECV_STATE_PROXYPROTOCOLHEADER_WAIT;
×
128
        break;
×
129
      }
130
    }
131
  }
132
};
4✔
133

134
Client::Client( ClientInterface& aInterface, Crypt::TCryptInfo& encryption, sockaddr& ipaddr,
4✔
135
                std::vector<boost::asio::ip::network_v4>& allowed_proxies )
4✔
136
    : ThreadedClient( encryption, *this, ipaddr, allowed_proxies ),
137
      acct( nullptr ),
4✔
138
      chr( nullptr ),
4✔
139
      Interface( aInterface ),
4✔
140
      ready( false ),
4✔
141
      listen_port( 0 ),
4✔
142
      aosresist( false ),
4✔
143
      pause_count( 0 ),
4✔
144
      gd( new ClientGameData ),
4✔
145
      instance_( ++instance_counter_ ),
4✔
146
      UOExpansionFlagClient( 0 ),
4✔
147
      ClientType( 0 ),
4✔
148
      next_movement( 0 ),
4✔
149
      movementsequence( 0 ),
4✔
150
      paused_( false )
8✔
151
{
152
  weakptr.set( this );  // store weakptr for usage in scripts (see EClientRefObjImp)
4✔
153

154
  // For bypassing cryptseed packet
155
  if ( Core::settingsManager.ssopt.use_edit_server )
4✔
156
  {
157
    recv_state = RECV_STATE_MSGTYPE_WAIT;
×
158
  }
159

160
  Interface.register_client( this );
4✔
161

162
  memset( &clientinfo_, 0, sizeof( clientinfo_ ) );
4✔
163
  memset( &versiondetail_, 0, sizeof( versiondetail_ ) );
4✔
164
}
4✔
165

166
Client::~Client()
8✔
167
{
168
  PreDelete();
4✔
169
  delete cryptengine;
4✔
170
}
8✔
171

172
void Client::init_crypto( void* nseed, int type )
2✔
173
{
174
  session()->cryptengine->Init( nseed, type );
2✔
175
}
2✔
176

177
void Client::unregister()
4✔
178
{
179
  auto findClient =
180
      std::find( Core::networkManager.clients.begin(), Core::networkManager.clients.end(), this );
4✔
181
  Core::networkManager.clients.erase( findClient );  // TODO: Make networkManager more OO
4✔
182
  Interface.deregister_client( this );
4✔
183
}
4✔
184

185
void Client::PreDelete()
4✔
186
{
187
  closeConnection();
4✔
188

189
  if ( chr != nullptr && chr->client == this )
4✔
190
  {
191
    if ( chr->logged_in() )
2✔
192
    {
193
      ClrCharacterWorldPosition( chr, Realms::WorldChangeReason::PlayerExit );
2✔
194
      send_remove_character_to_nearby( chr );
2✔
195
      chr->logged_in( false );
2✔
196

197
      chr->set_opponent( {} );
2✔
198
      chr->removal_cleanup();
2✔
199
      if ( chr->get_opponent() )
2✔
200
      {
NEW
201
        chr->set_opponent( {}, true );
×
202
      }
203
    }
204
    else
205
    {
206
      ERROR_PRINTLN( "Uhh...  active character not logged in!??" );
×
207
    }
208
  }
209

210
  // send closing event as long as client is attached to chr
211
  for ( const auto& gump : gd->gumpmods )
4✔
212
  {
213
    auto& [uoemod, event_based] = gump.second;
×
214
    if ( event_based )
×
215
      uoemod->uoexec().signal_event( new Module::GumpEvent( chr, new Bscript::BLong( 0 ) ) );
×
216
    else
217
      uoemod->uoexec().revive();
×
218
    std::erase_if( uoemod->gump_chrs, [&]( auto& e ) { return e.first == chr; } );
×
219
  }
220
  gd->gumpmods.clear();
4✔
221

222
  // detach the account and character from this client, if they
223
  // are still associated with it.
224

225
  acct = nullptr;
4✔
226

227
  if ( chr )
4✔
228
  {
229
    if ( chr->client == this )
2✔
230
      chr->client = nullptr;
2✔
231
    chr = nullptr;
2✔
232
  }
233

234
  // stop packet-logging
235
  stop_log();
4✔
236

237
  delete gd;
4✔
238
  gd = nullptr;
4✔
239

240
  while ( first_xmit_buffer != nullptr )
4✔
241
  {
242
    Core::XmitBuffer* xbuffer = first_xmit_buffer;
×
243
    first_xmit_buffer = first_xmit_buffer->next;
×
244
    free( xbuffer );
×
245
    --n_queued;
×
246
  }
247
  last_xmit_buffer = nullptr;
4✔
248
}
4✔
249

250
// ClientInfo - delivers a lot of usefull infomation about client PC
251
Bscript::BStruct* Client::getclientinfo() const
×
252
{
253
  using namespace Bscript;
254
  std::unique_ptr<BStruct> ret( new BStruct );
×
255

256
  ret->addMember( "unknown1", new BLong( clientinfo_.unknown1 ) );  // Unknown - allways 0x02
×
257
  ret->addMember( "instance", new BLong( clientinfo_.instance ) );  // Unique Instance ID of UO
×
258
  ret->addMember( "os_major", new BLong( clientinfo_.os_major ) );  // OS Major
×
259
  ret->addMember( "os_minor", new BLong( clientinfo_.os_minor ) );  // OS Minor
×
260
  ret->addMember( "os_revision", new BLong( clientinfo_.os_revision ) );  // OS Revision
×
261
  ret->addMember( "cpu_manufacturer",
×
262
                  new BLong( clientinfo_.cpu_manufacturer ) );          // CPU Manufacturer
×
263
  ret->addMember( "cpu_family", new BLong( clientinfo_.cpu_family ) );  // CPU Family
×
264
  ret->addMember( "cpu_model", new BLong( clientinfo_.cpu_model ) );    // CPU Model
×
265
  ret->addMember( "cpu_clockspeed",
×
266
                  new BLong( clientinfo_.cpu_clockspeed ) );                // CPU Clock Speed [Mhz]
×
267
  ret->addMember( "cpu_quantity", new BLong( clientinfo_.cpu_quantity ) );  // CPU Quantity
×
268
  ret->addMember( "memory", new BLong( clientinfo_.memory ) );              // Memory [MB]
×
269
  ret->addMember( "screen_width", new BLong( clientinfo_.screen_width ) );  // Screen Width
×
270
  ret->addMember( "screen_height", new BLong( clientinfo_.screen_height ) );  // Screen Height
×
271
  ret->addMember( "screen_depth", new BLong( clientinfo_.screen_depth ) );    // Screen Depth [Bit]
×
272
  ret->addMember( "directx_major", new BLong( clientinfo_.directx_major ) );  // DirectX Major
×
273
  ret->addMember( "directx_minor", new BLong( clientinfo_.directx_minor ) );  // DirectX Minor
×
274

275
  unsigned maxlen_vd =
×
276
      sizeof( clientinfo_.video_description ) / sizeof( clientinfo_.video_description[0] );
277
  std::string vd = Bscript::String::fromUTF16( &clientinfo_.video_description[0], maxlen_vd, true );
×
278
  ret->addMember( "video_description",
×
279
                  new Bscript::String( vd ) );  // Video Card Description
×
280

281

282
  ret->addMember( "video_vendor", new BLong( clientinfo_.video_vendor ) );  // Video Card Vendor ID
×
283
  ret->addMember( "video_device", new BLong( clientinfo_.video_device ) );  // Video Card Device ID
×
284
  ret->addMember( "video_memory",
×
285
                  new BLong( clientinfo_.video_memory ) );  // Video Card Memory [MB]
×
286
  ret->addMember( "distribution", new BLong( clientinfo_.distribution ) );        // Distribution
×
287
  ret->addMember( "clients_running", new BLong( clientinfo_.clients_running ) );  // Clients Running
×
288
  ret->addMember( "clients_installed",
×
289
                  new BLong( clientinfo_.clients_installed ) );  // Clients Installed
×
290
  ret->addMember( "partial_installed",
×
291
                  new BLong( clientinfo_.partial_installed ) );  // Partial Insstalled
×
292

293
  unsigned maxlen_lc = sizeof( clientinfo_.langcode ) / sizeof( clientinfo_.langcode[0] );
×
294
  std::string lc = Bscript::String::fromUTF16( &clientinfo_.langcode[0], maxlen_lc, true );
×
295
  ret->addMember( "langcode",
×
296
                  new Bscript::String( lc ) );  // Language Code
×
297

298
  std::unique_ptr<ObjArray> arr_u2( new ObjArray );
×
299
  for ( unsigned char i : clientinfo_.unknown2 )
×
300
    arr_u2->addElement( new BLong( i ) );
×
301
  ret->addMember( "unknown2", arr_u2.release() );  // Unknown
×
302

303
  return ret.release();
×
304
}
×
305

306
void Client::itemizeclientversion( const std::string& ver, VersionDetailStruct& detail )
2✔
307
{
308
  try
309
  {
310
    size_t dot1 = ver.find_first_of( '.', 0 );
2✔
311
    size_t dot2 = ver.find_first_of( '.', dot1 + 1 );
2✔
312
    size_t dot3 = ver.find_first_of( '.', dot2 + 1 );
2✔
313
    if ( dot3 == std::string::npos )  // since 5.0.7 patch is digit
2✔
314
    {
315
      dot3 = dot2 + 1;
×
316
      while ( ( dot3 < ver.length() ) && ( isdigit( ver[dot3] ) ) )
×
317
      {
318
        dot3++;
×
319
      }
320
    }
321

322
    detail.major = atoi( ver.substr( 0, dot1 ).c_str() );
2✔
323
    detail.minor = atoi( ver.substr( dot1 + 1, dot2 - dot1 - 1 ).c_str() );
2✔
324
    detail.rev = atoi( ver.substr( dot2 + 1, dot3 - dot2 - 1 ).c_str() );
2✔
325
    detail.patch = 0;
2✔
326
    if ( dot3 < ver.length() )
2✔
327
    {
328
      if ( ( detail.major <= 5 ) && ( detail.minor <= 0 ) && ( detail.rev <= 6 ) )
2✔
329
      {
330
        if ( ver[dot3] != ' ' )
×
331
          detail.patch = ( ver[dot3] - 'a' ) + 1;  // char to int
×
332
      }
333
      else
334
        detail.patch = atoi( ver.substr( dot3 + 1, ver.length() - dot3 - 1 ).c_str() );
2✔
335
    }
336
  }
337
  catch ( ... )
×
338
  {
339
    detail.major = 0;
×
340
    detail.minor = 0;
×
341
    detail.rev = 0;
×
342
    detail.patch = 0;
×
343
    POLLOGLN( "Malformed clientversion string: {}", ver );
×
344
  }
×
345
}
2✔
346

347
bool Client::compareVersion( const std::string& ver )
×
348
{
349
  VersionDetailStruct ver2;
350
  itemizeclientversion( ver, ver2 );
×
351
  return Client::compareVersion( ver2 );
×
352
}
353

354
bool Client::compareVersion( const VersionDetailStruct& ver2 )
16✔
355
{
356
  VersionDetailStruct ver1 = getversiondetail();
16✔
357

358
  if ( ver1.major > ver2.major )
16✔
359
    return true;
×
360
  if ( ver1.major < ver2.major )
16✔
361
    return false;
×
362
  if ( ver1.minor > ver2.minor )
16✔
363
    return true;
×
364
  if ( ver1.minor < ver2.minor )
16✔
365
    return false;
×
366
  if ( ver1.rev > ver2.rev )
16✔
367
    return true;
×
368
  if ( ver1.rev < ver2.rev )
16✔
369
    return false;
12✔
370
  if ( ver1.patch > ver2.patch )
4✔
371
    return true;
4✔
372
  if ( ver1.patch < ver2.patch )
×
373
    return false;
×
374
  return true;
×
375
}
376

377
void Client::setClientType( ClientTypeFlag type )
4✔
378
{
379
  ClientType = 0x0;
4✔
380
  // with fall through !
381
  switch ( type )
4✔
382
  {
383
  case CLIENTTYPE_70331:
×
384
    ClientType |= CLIENTTYPE_70331;
×
385
  // fall through
386
  case CLIENTTYPE_70300:
×
387
    ClientType |= CLIENTTYPE_70300;
×
388
  // fall through
389
  case CLIENTTYPE_70130:
×
390
    ClientType |= CLIENTTYPE_70130;
×
391
  // fall through
392
  case CLIENTTYPE_7090:
4✔
393
    ClientType |= CLIENTTYPE_7090;
4✔
394
  // fall through
395
  case CLIENTTYPE_UOSA:
4✔
396
    ClientType |= CLIENTTYPE_UOSA;
4✔
397
  // fall through
398
  case CLIENTTYPE_7000:
4✔
399
    ClientType |= CLIENTTYPE_7000;
4✔
400
  // fall through
401
  case CLIENTTYPE_UOKR:
4✔
402
    ClientType |= CLIENTTYPE_UOKR;
4✔
403
  // fall through
404
  case CLIENTTYPE_60142:
4✔
405
    ClientType |= CLIENTTYPE_60142;
4✔
406
  // fall through
407
  case CLIENTTYPE_6017:
4✔
408
    ClientType |= CLIENTTYPE_6017;
4✔
409
  // fall through
410
  case CLIENTTYPE_5020:
4✔
411
    ClientType |= CLIENTTYPE_5020;
4✔
412
  // fall through
413
  case CLIENTTYPE_5000:
4✔
414
    ClientType |= CLIENTTYPE_5000;
4✔
415
  // fall through
416
  case CLIENTTYPE_4070:
4✔
417
    ClientType |= CLIENTTYPE_4070;
4✔
418
  // fall through
419
  case CLIENTTYPE_4000:
4✔
420
    ClientType |= CLIENTTYPE_4000;
4✔
421
  // fall through
422
  default:
4✔
423
    break;
4✔
424
  }
425
}
4✔
426

427
bool Client::IsUOKRClient()
×
428
{
429
  return ( ( ClientType & CLIENTTYPE_UOKR ) && ( !( ClientType & CLIENTTYPE_7000 ) ) );
×
430
}
431

432
std::string Client::status() const
×
433
{
434
  std::string st;
×
435
  if ( acct != nullptr )
×
436
    st += "AC:" + std::string( acct->name() ) + " ";
×
437
  if ( chr != nullptr )
×
438
    st += "CH:" + chr->name() + " ";
×
439
  if ( have_queued_data() )
×
440
    st += "TXBUF ";
×
441
  if ( disconnect )
×
442
    st += "DISC ";
×
443
  if ( paused_ )
×
444
    st += "PAUSE ";
×
445
  if ( ready )
×
446
    st += "RDY ";
×
447
  st += ipaddrAsString() + " ";
×
448
  st += "CHK: " + Clib::tostring( checkpoint ) + " ";
×
449
  st += "PID: " + Clib::tostring( thread_pid ) + " ";
×
450
  st += "LAST: " + Clib::hexint( last_msgtype );
×
451
  return st;
×
452
}
×
453

454
void ThreadedClient::queue_data( const void* data, unsigned short datalen )
×
455
{
456
  THREAD_CHECKPOINT( active_client, 300 );
×
457
  Core::XmitBuffer* xbuffer = (Core::XmitBuffer*)malloc( sizeof( Core::XmitBuffer ) - 1 + datalen );
×
458
  THREAD_CHECKPOINT( active_client, 301 );
×
459
  if ( xbuffer )
×
460
  {
461
    THREAD_CHECKPOINT( active_client, 302 );
×
462
    xbuffer->next = nullptr;
×
463
    xbuffer->nsent = 0;
×
464
    xbuffer->lenleft = datalen;
×
465
    memcpy( xbuffer->data, data, datalen );
×
466
    THREAD_CHECKPOINT( active_client, 303 );
×
467
    if ( first_xmit_buffer == nullptr || last_xmit_buffer == nullptr )
×
468
    {  // in this case, last_xmit_buffer is also nullptr, so can't set its ->next.
469
      THREAD_CHECKPOINT( active_client, 304 );
×
470
      first_xmit_buffer = xbuffer;
×
471
    }
472
    else
473
    {
474
      THREAD_CHECKPOINT( active_client, 305 );
×
475
      last_xmit_buffer->next = xbuffer;
×
476
    }
477
    THREAD_CHECKPOINT( active_client, 306 );
×
478
    last_xmit_buffer = xbuffer;
×
479
    ++n_queued;
×
480
    queued_bytes_counter += datalen;
×
481
  }
482
  else
483
  {
484
    THREAD_CHECKPOINT( active_client, 307 );
×
485
    POLLOGLN( "Client#{}: Unable to allocate {} bytes for queued data.  Disconnecting.",
×
486
              myClient.instance_, ( sizeof( Core::XmitBuffer ) - 1 + datalen ) );
×
487
    disconnect = true;
×
488
  }
489
  THREAD_CHECKPOINT( active_client, 309 );
×
490
}
×
491

492
void ThreadedClient::xmit( const void* data, unsigned short datalen )
19,633✔
493
{
494
  if ( csocket == INVALID_SOCKET )
19,633✔
495
    return;
×
496
  if ( encrypt_server_stream )
19,633✔
497
  {
498
    if ( cryptengine == nullptr )
19,629✔
499
      return;
×
500
    this->cryptengine->Encrypt( (void*)data, (void*)data, datalen );
19,629✔
501
  }
502
  THREAD_CHECKPOINT( active_client, 200 );
19,633✔
503
  if ( last_xmit_buffer )  // this client already backlogged, schedule for later
19,633✔
504
  {
505
    THREAD_CHECKPOINT( active_client, 201 );
×
506
    queue_data( data, datalen );
×
507
    THREAD_CHECKPOINT( active_client, 202 );
×
508
    return;
×
509
  }
510
  THREAD_CHECKPOINT( active_client, 203 );
19,633✔
511

512
  /* client not backlogged - try to send. */
513
  const unsigned char* cdata = (const unsigned char*)data;
19,633✔
514
  int nsent;
515

516
  if ( -1 == ( nsent = send( csocket, (const char*)cdata, datalen, 0 ) ) )
19,633✔
517
  {
518
    THREAD_CHECKPOINT( active_client, 204 );
×
519
    int sckerr = socket_errno;
×
520

521
    if ( sckerr == SOCKET_ERRNO( EWOULDBLOCK ) )
×
522
    {
523
      THREAD_CHECKPOINT( active_client, 205 );
×
524
      POLLOG_ERRORLN( "Client#{}: Switching to queued data mode (1, {} bytes)", myClient.instance_,
×
525
                      datalen );
526
      THREAD_CHECKPOINT( active_client, 206 );
×
527
      queue_data( data, datalen );
×
528
      THREAD_CHECKPOINT( active_client, 207 );
×
529
      return;
×
530
    }
531

532
    THREAD_CHECKPOINT( active_client, 208 );
×
533
    if ( !disconnect )
×
534
      POLLOG_ERRORLN( "Client#{}: Disconnecting client due to send() error (1): {}",
×
535
                      myClient.instance_, sckerr );
×
536
    disconnect = true;
×
537
    THREAD_CHECKPOINT( active_client, 209 );
×
538
    return;
×
539
  }
540
  // no error
541
  THREAD_CHECKPOINT( active_client, 210 );
19,633✔
542
  datalen -= static_cast<unsigned short>( nsent );
19,633✔
543
  counters.bytes_transmitted += nsent;
19,633✔
544
  Core::networkManager.polstats.bytes_sent += nsent;
19,633✔
545
  if ( datalen )  // anything left? if so, queue for later.
19,633✔
546
  {
547
    THREAD_CHECKPOINT( active_client, 211 );
×
548
    POLLOG_ERRORLN( "Client#{}: Switching to queued data mode (2)", myClient.instance_ );
×
549
    THREAD_CHECKPOINT( active_client, 212 );
×
550
    queue_data( cdata + nsent, datalen );
×
551
    THREAD_CHECKPOINT( active_client, 213 );
×
552
  }
553

554
  THREAD_CHECKPOINT( active_client, 214 );
19,633✔
555
}
556

557
void ThreadedClient::send_queued_data()
×
558
{
559
  std::lock_guard<std::mutex> lock( _socketMutex );
×
560
  Core::XmitBuffer* xbuffer;
561
  // hand off data to the sockets layer until it won't take any more.
562
  // note if a buffer is sent in full, we try to send the next one, ad infinitum
563
  while ( nullptr != ( xbuffer = first_xmit_buffer ) )
×
564
  {
565
    int nsent;
566
    nsent = send( csocket, (char*)&xbuffer->data[xbuffer->nsent], xbuffer->lenleft, 0 );
×
567
    if ( nsent == -1 )
×
568
    {
569
#ifdef _WIN32
570
      int sckerr = WSAGetLastError();
571
#else
572
      int sckerr = errno;
×
573
#endif
574
      if ( sckerr == SOCKET_ERRNO( EWOULDBLOCK ) )
×
575
      {
576
        // do nothing.  it'll be re-queued later, when it won't block.
577
        return;
×
578
      }
579

580
      if ( !disconnect )
×
581
        POLLOGLN( "Client#{}: Disconnecting client due to send() error (2): {}", myClient.instance_,
×
582
                  sckerr );
583
      disconnect = true;
×
584
      return;
×
585
    }
586

587
    xbuffer->nsent += static_cast<unsigned short>( nsent );
×
588
    xbuffer->lenleft -= static_cast<unsigned short>( nsent );
×
589
    counters.bytes_transmitted += nsent;
×
590
    Core::networkManager.polstats.bytes_sent += nsent;
×
591
    if ( xbuffer->lenleft == 0 )
×
592
    {
593
      first_xmit_buffer = first_xmit_buffer->next;
×
594
      if ( first_xmit_buffer == nullptr )
×
595
      {
596
        last_xmit_buffer = nullptr;
×
597
        POLLOGLN( "Client#{}: Leaving queued mode ({} bytes xmitted)", myClient.instance_,
×
598
                  queued_bytes_counter );
×
599
        queued_bytes_counter = 0;
×
600
      }
601
      free( xbuffer );
×
602
      --n_queued;
×
603
    }
604
  }
605
}
×
606

607
// 33 01 "encrypted": 4F FA
608
static const unsigned char pause_pre_encrypted[2] = { 0x4F, 0xFA };
609
// 33 00 "encrypted": 4C D0
610
static const unsigned char restart_pre_encrypted[2] = { 0x4C, 0xD0 };
611

612
void Client::send_pause()
38✔
613
{
614
  if ( Core::networkManager.uoclient_protocol.EnableFlowControlPackets && !paused_ )
38✔
615
  {
616
#ifndef PRE_ENCRYPT
617
    PKTOUT_33 msg;
618
    msg.msgtype = PKTOUT_33_ID;
619
    msg.flow = MSGOPT_33_FLOW_PAUSE;
620
    transmit( this, &msg, sizeof msg );
621
#else
622
    xmit( pause_pre_encrypted, sizeof pause_pre_encrypted );
×
623
#endif
624
    paused_ = true;
×
625
  }
626
}
38✔
627

628
void Client::pause()
19,665✔
629
{
630
  if ( !pause_count )
19,665✔
631
  {
632
    send_pause();
38✔
633
    pause_count = 1;
38✔
634
  }
635
}
19,665✔
636

637
void Client::send_restart()
36✔
638
{
639
  if ( paused_ )
36✔
640
  {
641
#ifndef PRE_ENCRYPT
642
    PKTOUT_33 msg;
643
    msg.msgtype = PKTOUT_33_ID;
644
    msg.flow = MSGOPT_33_FLOW_RESTART;
645
    transmit( this, &msg, sizeof msg );
646
#else
647
    xmit( restart_pre_encrypted, sizeof restart_pre_encrypted );
×
648
#endif
649
    paused_ = false;
×
650
  }
651
}
36✔
652

653
void Client::restart()
36✔
654
{
655
  send_restart();
36✔
656
  pause_count = 0;
36✔
657
}
36✔
658

659
// Note: this doesnt test single packets it only summs the delay and tests
660
// here only the "start"-value is set the additional delay is set in PKT_02 handler
661
bool Client::SpeedHackPrevention( bool add )
×
662
{
663
  if ( ( !movementqueue.empty() ) && ( add ) )
×
664
  {
665
    if ( movementqueue.size() > 100 )
×
666
    {
667
      POLLOG_ERRORLN( "Client#{}: More then 100 Movepackets in queue.  Disconnecting.", instance_ );
×
668
      disconnect = true;
×
669
      return false;
×
670
    }
671
    PacketThrottler throttlestruct;
672
    memcpy( &throttlestruct.pktbuffer, &buffer, PKTIN_02_SIZE );
×
673
    movementqueue.push( throttlestruct );
×
674
    return false;
×
675
  }
676
  if ( chr != nullptr && chr->can_speedhack() )
×
677
    return true;
×
678
  if ( ( next_movement == 0 ) ||
×
679
       ( Clib::wallclock() > next_movement ) )  // never moved or in the past
×
680
  {
681
    next_movement = Clib::wallclock();
×
682
    return true;
×
683
  }
684
  // now we dont alter next_movement so we can sum the delay till diff is greater then error margin
685
  Clib::wallclock_diff_t diff = Clib::wallclock_diff_ms( Clib::wallclock(), next_movement );
×
686
  if ( diff > PKTIN_02_ASYNCHRONOUS )  // delay sum greater then our error margin?
×
687
  {
688
    if ( add )  // delay packet
×
689
    {
690
      if ( movementqueue.size() > 100 )
×
691
      {
692
        POLLOG_ERRORLN( "Client#{}: More then 100 Movepackets in queue.  Disconnecting.",
×
693
                        instance_ );
×
694
        disconnect = true;
×
695
        return false;
×
696
      }
697
      PacketThrottler throttlestruct;
698
      memcpy( &throttlestruct.pktbuffer, &buffer, sizeof( throttlestruct.pktbuffer ) );
×
699
      movementqueue.push( throttlestruct );
×
700
    }
701
    return false;
×
702
  }
703
  return true;
×
704
}
705

706
Bscript::BObjectImp* Client::make_ref()
11✔
707
{
708
  return new Module::EClientRefObjImp( weakptr );
11✔
709
}
710

711
weak_ptr<Client> Client::getWeakPtr() const
19,637✔
712
{
713
  return weakptr;
19,637✔
714
}
715
void Client::set_update_range_by_client( u8 range )
2✔
716
{
717
  // only allow a change if allowed and not modified by script
718
  if ( Core::settingsManager.ssopt.allow_visual_range_modification )
2✔
719
  {
720
    // limit range to defined min/max
721
    range = std::clamp( range, Core::settingsManager.ssopt.min_visual_range,
×
722
                        Core::settingsManager.ssopt.max_visual_range );
723
    if ( !gd->script_defined_update_range )
×
724
      set_update_range( range );
×
725
    gd->original_client_update_range = range;
×
726
  }
727
  else
728
  {
729
    PktHelper::PacketOut<Network::PktOut_C8> outMsg;
2✔
730
    outMsg->Write<u8>( update_range() );
2✔
731
    outMsg.Send( this );
2✔
732
    gd->original_client_update_range = Core::settingsManager.ssopt.default_visual_range;
2✔
733
  }
2✔
734
}
2✔
735

736
void Client::set_update_range_by_script( u8 range )
2✔
737
{
738
  if ( range == 0 )
2✔
739
  {
740
    range = gd->original_client_update_range ? gd->original_client_update_range
1✔
741
                                             : Core::settingsManager.ssopt.default_visual_range;
742
    gd->script_defined_update_range = false;
1✔
743
  }
744
  else
745
  {
746
    gd->script_defined_update_range = true;
1✔
747
  }
748
  // no limit check here, script is allowed to use different values
749
  set_update_range( range );
2✔
750
}
2✔
751

752
void Client::set_update_range( u8 range )
2✔
753
{
754
  auto old_range = update_range();
2✔
755
  // first signal the client about the change before we potentially send new objects otherwise the
756
  // client could directly drop them
757
  PktHelper::PacketOut<PktOut_C8> outMsg;
2✔
758
  outMsg->Write<u8>( range );
2✔
759
  outMsg.Send( this );
2✔
760

761
  // delete/send all objects, but not if its the first pkt
762
  if ( old_range != range && gd->original_client_update_range != 0 )
2✔
763
    chr->update_objects_on_range_change( range );
2✔
764

765
  gd->update_range = range;
2✔
766

767
  if ( old_range != range )
2✔
768
  {
769
    // update global updaterange (maximum multi radius/client view range)
770
    Core::gamestate.update_range_from_client( range );
2✔
771
  }
772
}
2✔
773

774
u8 Client::update_range() const
63,020✔
775
{
776
  return gd->update_range;
63,020✔
777
}
778

779
bool Client::acctSupports( Plib::ExpansionVersion v ) const
6,888✔
780
{
781
  return acct->expansion().hasExpansion( v );
6,888✔
782
}
783

784
size_t Client::estimatedSize() const
×
785
{
786
  Clib::SpinLockGuard guard( _fpLog_lock );
×
787
  size_t size = ThreadedClient::estimatedSize();
×
788
  size += sizeof( void* ) * 3                              /*acct, chr, Interface*/
×
789
          + sizeof( bool )                                 /* ready */
790
          + sizeof( unsigned short )                       /* listenpoint */
791
          + sizeof( bool )                                 /* aosresist */
792
          + sizeof( std::atomic<int> )                     /* pause_count */
793
          + sizeof( void* )                                /* gd */
794
          + sizeof( unsigned int )                         /* instance_ */
795
          + sizeof( u32 )                                  /* UOExpansionFlagClient */
796
          + sizeof( u16 )                                  /* ClientType */
797
          + sizeof( Clib::wallclock_t )                    /* next_movement */
798
          + sizeof( u8 )                                   /* movementsequence */
799
          + version_.capacity() + sizeof( Core::PKTIN_D9 ) /* clientinfo_*/
×
800
          + sizeof( bool )                                 /* paused_ */
801
          + sizeof( VersionDetailStruct )                  /* versiondetail_ */
802
          + sizeof( weak_ptr_owner<Client> )               /*weakptr*/
×
803
      ;
804
  size += Clib::memsize( movementqueue );
×
805
  if ( gd != nullptr )
×
806
    size += gd->estimatedSize();
×
807
  return size;
×
808
}
×
809

810
// Threaded client stuff
811

812
size_t ThreadedClient::estimatedSize() const
×
813
{
814
  size_t size = sizeof( ThreadedClient ) + Clib::memsize( allowed_proxies ) + fpLog.capacity();
×
815
  Core::XmitBuffer* buffer_size = first_xmit_buffer;
×
816
  while ( buffer_size != nullptr )
×
817
  {
818
    size += sizeof( buffer_size ) + buffer_size->lenleft;
×
819
    buffer_size = buffer_size->next;
×
820
  }
821
  return size;
×
822
}
823
void ThreadedClient::closeConnection()
7✔
824
{
825
  std::lock_guard<std::mutex> lock( _socketMutex );
7✔
826
  if ( csocket != INVALID_SOCKET )
8✔
827
  {
828
#ifdef _WIN32
829
    shutdown( csocket, 2 );  // 2 is both sides, defined in winsock2.h ...
830
    closesocket( csocket );
831
#else
832
    shutdown( csocket, SHUT_RDWR );
4✔
833
    close( csocket );
4✔
834
#endif
835
  }
836
  csocket = INVALID_SOCKET;
8✔
837
}
8✔
838

839

840
}  // namespace Network
841
}  // namespace Pol
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc