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

polserver / polserver / 21108840797

18 Jan 2026 08:35AM UTC coverage: 60.508% (+0.02%) from 60.492%
21108840797

push

github

web-flow
ClangTidy readability-else-after-return (#857)

* trigger tidy

* Automated clang-tidy change: readability-else-after-return

* compile test

* rerun

* Automated clang-tidy change: readability-else-after-return

* trigger..

* Automated clang-tidy change: readability-else-after-return

* manually removed a few

* Automated clang-tidy change: readability-else-after-return

* removed duplicate code

* fix remaining warnings

* fixed scope

---------

Co-authored-by: Clang Tidy <clang-tidy@users.noreply.github.com>

837 of 1874 new or added lines in 151 files covered. (44.66%)

46 existing lines in 25 files now uncovered.

44448 of 73458 relevant lines covered (60.51%)

525066.38 hits per line

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

47.45
/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 "../polsig.h"
42
#include "../realms/WorldChangeReasons.h"
43
#include "../ufunc.h"  // only in here temporarily, until logout-on-disconnect stuff is removed
44
#include "../uoclient.h"
45
#include "../uoscrobj.h"
46
#include "../uworld.h"
47
#include "cgdata.h"
48
#include "cliface.h"
49
#include "packethelper.h"
50
#include "packets.h"
51
#include "pktdef.h"
52
#include "pktin.h"
53
#include "xbuffer.h"
54

55

56
#define PRE_ENCRYPT
57

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

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

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

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

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

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

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

159
  Interface.register_client( this );
4✔
160

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

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

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

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

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

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

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

209
  // detach the account and character from this client, if they
210
  // are still associated with it.
211

212
  acct = nullptr;
4✔
213

214
  if ( chr )
4✔
215
  {
216
    if ( chr->client == this )
2✔
217
      chr->client = nullptr;
2✔
218
    chr = nullptr;
2✔
219
  }
220

221
  // stop packet-logging
222
  stop_log();
4✔
223

224
  delete gd;
4✔
225
  gd = nullptr;
4✔
226

227
  while ( first_xmit_buffer != nullptr )
4✔
228
  {
229
    Core::XmitBuffer* xbuffer = first_xmit_buffer;
×
230
    first_xmit_buffer = first_xmit_buffer->next;
×
231
    free( xbuffer );
×
232
    --n_queued;
×
233
  }
234
  last_xmit_buffer = nullptr;
4✔
235

236
  // while (!movementqueue.empty())
237
  //  movementqueue.pop();
238
}
4✔
239

240
// ClientInfo - delivers a lot of usefull infomation about client PC
241
Bscript::BStruct* Client::getclientinfo() const
×
242
{
243
  using namespace Bscript;
244
  std::unique_ptr<BStruct> ret( new BStruct );
×
245

246
  ret->addMember( "unknown1", new BLong( clientinfo_.unknown1 ) );  // Unknown - allways 0x02
×
247
  ret->addMember( "instance", new BLong( clientinfo_.instance ) );  // Unique Instance ID of UO
×
248
  ret->addMember( "os_major", new BLong( clientinfo_.os_major ) );  // OS Major
×
249
  ret->addMember( "os_minor", new BLong( clientinfo_.os_minor ) );  // OS Minor
×
250
  ret->addMember( "os_revision", new BLong( clientinfo_.os_revision ) );  // OS Revision
×
251
  ret->addMember( "cpu_manufacturer",
×
252
                  new BLong( clientinfo_.cpu_manufacturer ) );          // CPU Manufacturer
×
253
  ret->addMember( "cpu_family", new BLong( clientinfo_.cpu_family ) );  // CPU Family
×
254
  ret->addMember( "cpu_model", new BLong( clientinfo_.cpu_model ) );    // CPU Model
×
255
  ret->addMember( "cpu_clockspeed",
×
256
                  new BLong( clientinfo_.cpu_clockspeed ) );                // CPU Clock Speed [Mhz]
×
257
  ret->addMember( "cpu_quantity", new BLong( clientinfo_.cpu_quantity ) );  // CPU Quantity
×
258
  ret->addMember( "memory", new BLong( clientinfo_.memory ) );              // Memory [MB]
×
259
  ret->addMember( "screen_width", new BLong( clientinfo_.screen_width ) );  // Screen Width
×
260
  ret->addMember( "screen_height", new BLong( clientinfo_.screen_height ) );  // Screen Height
×
261
  ret->addMember( "screen_depth", new BLong( clientinfo_.screen_depth ) );    // Screen Depth [Bit]
×
262
  ret->addMember( "directx_major", new BLong( clientinfo_.directx_major ) );  // DirectX Major
×
263
  ret->addMember( "directx_minor", new BLong( clientinfo_.directx_minor ) );  // DirectX Minor
×
264

265
  unsigned maxlen_vd =
×
266
      sizeof( clientinfo_.video_description ) / sizeof( clientinfo_.video_description[0] );
267
  std::string vd = Bscript::String::fromUTF16( &clientinfo_.video_description[0], maxlen_vd, true );
×
268
  ret->addMember( "video_description",
×
269
                  new Bscript::String( vd ) );  // Video Card Description
×
270

271

272
  ret->addMember( "video_vendor", new BLong( clientinfo_.video_vendor ) );  // Video Card Vendor ID
×
273
  ret->addMember( "video_device", new BLong( clientinfo_.video_device ) );  // Video Card Device ID
×
274
  ret->addMember( "video_memory",
×
275
                  new BLong( clientinfo_.video_memory ) );  // Video Card Memory [MB]
×
276
  ret->addMember( "distribution", new BLong( clientinfo_.distribution ) );        // Distribution
×
277
  ret->addMember( "clients_running", new BLong( clientinfo_.clients_running ) );  // Clients Running
×
278
  ret->addMember( "clients_installed",
×
279
                  new BLong( clientinfo_.clients_installed ) );  // Clients Installed
×
280
  ret->addMember( "partial_installed",
×
281
                  new BLong( clientinfo_.partial_installed ) );  // Partial Insstalled
×
282

283
  unsigned maxlen_lc = sizeof( clientinfo_.langcode ) / sizeof( clientinfo_.langcode[0] );
×
284
  std::string lc = Bscript::String::fromUTF16( &clientinfo_.langcode[0], maxlen_lc, true );
×
285
  ret->addMember( "langcode",
×
286
                  new Bscript::String( lc ) );  // Language Code
×
287

288
  std::unique_ptr<ObjArray> arr_u2( new ObjArray );
×
289
  for ( unsigned i = 0; i < sizeof( clientinfo_.unknown2 ); ++i )
×
290
    arr_u2->addElement( new BLong( clientinfo_.unknown2[i] ) );
×
291
  ret->addMember( "unknown2", arr_u2.release() );  // Unknown
×
292

293
  return ret.release();
×
294
}
×
295

296
void Client::itemizeclientversion( const std::string& ver, VersionDetailStruct& detail )
2✔
297
{
298
  try
299
  {
300
    size_t dot1 = ver.find_first_of( '.', 0 );
2✔
301
    size_t dot2 = ver.find_first_of( '.', dot1 + 1 );
2✔
302
    size_t dot3 = ver.find_first_of( '.', dot2 + 1 );
2✔
303
    if ( dot3 == std::string::npos )  // since 5.0.7 patch is digit
2✔
304
    {
305
      dot3 = dot2 + 1;
×
306
      while ( ( dot3 < ver.length() ) && ( isdigit( ver[dot3] ) ) )
×
307
      {
308
        dot3++;
×
309
      }
310
    }
311

312
    detail.major = atoi( ver.substr( 0, dot1 ).c_str() );
2✔
313
    detail.minor = atoi( ver.substr( dot1 + 1, dot2 - dot1 - 1 ).c_str() );
2✔
314
    detail.rev = atoi( ver.substr( dot2 + 1, dot3 - dot2 - 1 ).c_str() );
2✔
315
    detail.patch = 0;
2✔
316
    if ( dot3 < ver.length() )
2✔
317
    {
318
      if ( ( detail.major <= 5 ) && ( detail.minor <= 0 ) && ( detail.rev <= 6 ) )
2✔
319
      {
320
        if ( ver[dot3] != ' ' )
×
321
          detail.patch = ( ver[dot3] - 'a' ) + 1;  // char to int
×
322
      }
323
      else
324
        detail.patch = atoi( ver.substr( dot3 + 1, ver.length() - dot3 - 1 ).c_str() );
2✔
325
    }
326
  }
327
  catch ( ... )
×
328
  {
329
    detail.major = 0;
×
330
    detail.minor = 0;
×
331
    detail.rev = 0;
×
332
    detail.patch = 0;
×
333
    POLLOGLN( "Malformed clientversion string: {}", ver );
×
334
  }
×
335
}
2✔
336

337
bool Client::compareVersion( const std::string& ver )
×
338
{
339
  VersionDetailStruct ver2;
340
  itemizeclientversion( ver, ver2 );
×
341
  return Client::compareVersion( ver2 );
×
342
}
343

344
bool Client::compareVersion( const VersionDetailStruct& ver2 )
16✔
345
{
346
  VersionDetailStruct ver1 = getversiondetail();
16✔
347

348
  if ( ver1.major > ver2.major )
16✔
349
    return true;
×
350
  if ( ver1.major < ver2.major )
16✔
351
    return false;
×
352
  if ( ver1.minor > ver2.minor )
16✔
353
    return true;
×
354
  if ( ver1.minor < ver2.minor )
16✔
355
    return false;
×
356
  if ( ver1.rev > ver2.rev )
16✔
357
    return true;
×
358
  if ( ver1.rev < ver2.rev )
16✔
359
    return false;
12✔
360
  if ( ver1.patch > ver2.patch )
4✔
361
    return true;
4✔
NEW
362
  if ( ver1.patch < ver2.patch )
×
363
    return false;
×
NEW
364
  return true;
×
365
}
366

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

417
bool Client::IsUOKRClient()
×
418
{
419
  return ( ( ClientType & CLIENTTYPE_UOKR ) && ( !( ClientType & CLIENTTYPE_7000 ) ) );
×
420
}
421

422
std::string Client::status() const
×
423
{
424
  std::string st;
×
425
  if ( acct != nullptr )
×
426
    st += "AC:" + std::string( acct->name() ) + " ";
×
427
  if ( chr != nullptr )
×
428
    st += "CH:" + chr->name() + " ";
×
429
  if ( have_queued_data() )
×
430
    st += "TXBUF ";
×
431
  if ( disconnect )
×
432
    st += "DISC ";
×
433
  if ( paused_ )
×
434
    st += "PAUSE ";
×
435
  if ( ready )
×
436
    st += "RDY ";
×
437
  st += ipaddrAsString() + " ";
×
438
  st += "CHK: " + Clib::tostring( checkpoint ) + " ";
×
439
  st += "PID: " + Clib::tostring( thread_pid ) + " ";
×
440
  st += "LAST: " + Clib::hexint( last_msgtype );
×
441
  return st;
×
442
}
×
443

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

482
void ThreadedClient::xmit( const void* data, unsigned short datalen )
19,628✔
483
{
484
  if ( csocket == INVALID_SOCKET )
19,628✔
485
    return;
×
486
  if ( encrypt_server_stream )
19,628✔
487
  {
488
    if ( cryptengine == nullptr )
19,624✔
489
      return;
×
490
    this->cryptengine->Encrypt( (void*)data, (void*)data, datalen );
19,624✔
491
  }
492
  THREAD_CHECKPOINT( active_client, 200 );
19,628✔
493
  if ( last_xmit_buffer )  // this client already backlogged, schedule for later
19,628✔
494
  {
495
    THREAD_CHECKPOINT( active_client, 201 );
×
496
    queue_data( data, datalen );
×
497
    THREAD_CHECKPOINT( active_client, 202 );
×
498
    return;
×
499
  }
500
  THREAD_CHECKPOINT( active_client, 203 );
19,628✔
501

502
  /* client not backlogged - try to send. */
503
  const unsigned char* cdata = (const unsigned char*)data;
19,628✔
504
  int nsent;
505

506
  if ( -1 == ( nsent = send( csocket, (const char*)cdata, datalen, 0 ) ) )
19,628✔
507
  {
508
    THREAD_CHECKPOINT( active_client, 204 );
×
509
    int sckerr = socket_errno;
×
510

511
    if ( sckerr == SOCKET_ERRNO( EWOULDBLOCK ) )
×
512
    {
513
      THREAD_CHECKPOINT( active_client, 205 );
×
514
      POLLOG_ERRORLN( "Client#{}: Switching to queued data mode (1, {} bytes)", myClient.instance_,
×
515
                      datalen );
516
      THREAD_CHECKPOINT( active_client, 206 );
×
517
      queue_data( data, datalen );
×
518
      THREAD_CHECKPOINT( active_client, 207 );
×
519
      return;
×
520
    }
521

NEW
522
    THREAD_CHECKPOINT( active_client, 208 );
×
NEW
523
    if ( !disconnect )
×
NEW
524
      POLLOG_ERRORLN( "Client#{}: Disconnecting client due to send() error (1): {}",
×
NEW
525
                      myClient.instance_, sckerr );
×
NEW
526
    disconnect = true;
×
NEW
527
    THREAD_CHECKPOINT( active_client, 209 );
×
NEW
528
    return;
×
529
  }
530
  // no error
531
  THREAD_CHECKPOINT( active_client, 210 );
19,628✔
532
  datalen -= static_cast<unsigned short>( nsent );
19,628✔
533
  counters.bytes_transmitted += nsent;
19,628✔
534
  Core::networkManager.polstats.bytes_sent += nsent;
19,628✔
535
  if ( datalen )  // anything left? if so, queue for later.
19,628✔
536
  {
NEW
537
    THREAD_CHECKPOINT( active_client, 211 );
×
NEW
538
    POLLOG_ERRORLN( "Client#{}: Switching to queued data mode (2)", myClient.instance_ );
×
NEW
539
    THREAD_CHECKPOINT( active_client, 212 );
×
NEW
540
    queue_data( cdata + nsent, datalen );
×
NEW
541
    THREAD_CHECKPOINT( active_client, 213 );
×
542
  }
543

544
  THREAD_CHECKPOINT( active_client, 214 );
19,628✔
545
}
546

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

NEW
570
      if ( !disconnect )
×
NEW
571
        POLLOGLN( "Client#{}: Disconnecting client due to send() error (2): {}", myClient.instance_,
×
572
                  sckerr );
NEW
573
      disconnect = true;
×
NEW
574
      return;
×
575
    }
576

NEW
577
    xbuffer->nsent += static_cast<unsigned short>( nsent );
×
NEW
578
    xbuffer->lenleft -= static_cast<unsigned short>( nsent );
×
NEW
579
    counters.bytes_transmitted += nsent;
×
NEW
580
    Core::networkManager.polstats.bytes_sent += nsent;
×
NEW
581
    if ( xbuffer->lenleft == 0 )
×
582
    {
NEW
583
      first_xmit_buffer = first_xmit_buffer->next;
×
NEW
584
      if ( first_xmit_buffer == nullptr )
×
585
      {
NEW
586
        last_xmit_buffer = nullptr;
×
NEW
587
        POLLOGLN( "Client#{}: Leaving queued mode ({} bytes xmitted)", myClient.instance_,
×
NEW
588
                  queued_bytes_counter );
×
NEW
589
        queued_bytes_counter = 0;
×
590
      }
NEW
591
      free( xbuffer );
×
NEW
592
      --n_queued;
×
593
    }
594
  }
595
}
×
596

597
// 33 01 "encrypted": 4F FA
598
static const unsigned char pause_pre_encrypted[2] = { 0x4F, 0xFA };
599
// 33 00 "encrypted": 4C D0
600
static const unsigned char restart_pre_encrypted[2] = { 0x4C, 0xD0 };
601

602
void Client::send_pause()
36✔
603
{
604
  if ( Core::networkManager.uoclient_protocol.EnableFlowControlPackets && !paused_ )
36✔
605
  {
606
#ifndef PRE_ENCRYPT
607
    PKTOUT_33 msg;
608
    msg.msgtype = PKTOUT_33_ID;
609
    msg.flow = MSGOPT_33_FLOW_PAUSE;
610
    transmit( this, &msg, sizeof msg );
611
#else
612
    xmit( pause_pre_encrypted, sizeof pause_pre_encrypted );
×
613
#endif
614
    paused_ = true;
×
615
  }
616
}
36✔
617

618
void Client::pause()
19,658✔
619
{
620
  if ( !pause_count )
19,658✔
621
  {
622
    send_pause();
36✔
623
    pause_count = 1;
36✔
624
  }
625
}
19,658✔
626

627
void Client::send_restart()
34✔
628
{
629
  if ( paused_ )
34✔
630
  {
631
#ifndef PRE_ENCRYPT
632
    PKTOUT_33 msg;
633
    msg.msgtype = PKTOUT_33_ID;
634
    msg.flow = MSGOPT_33_FLOW_RESTART;
635
    transmit( this, &msg, sizeof msg );
636
#else
637
    xmit( restart_pre_encrypted, sizeof restart_pre_encrypted );
×
638
#endif
639
    paused_ = false;
×
640
  }
641
}
34✔
642

643
void Client::restart()
34✔
644
{
645
  send_restart();
34✔
646
  pause_count = 0;
34✔
647
}
34✔
648

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

696
Bscript::BObjectImp* Client::make_ref()
11✔
697
{
698
  return new Module::EClientRefObjImp( weakptr );
11✔
699
}
700

701
weak_ptr<Client> Client::getWeakPtr() const
19,632✔
702
{
703
  return weakptr;
19,632✔
704
}
705
void Client::set_update_range_by_client( u8 range )
2✔
706
{
707
  // only allow a change if allowed and not modified by script
708
  if ( Core::settingsManager.ssopt.allow_visual_range_modification )
2✔
709
  {
710
    // limit range to defined min/max
711
    range = std::clamp( range, Core::settingsManager.ssopt.min_visual_range,
×
712
                        Core::settingsManager.ssopt.max_visual_range );
713
    if ( !gd->script_defined_update_range )
×
714
      set_update_range( range );
×
715
    gd->original_client_update_range = range;
×
716
  }
717
  else
718
  {
719
    PktHelper::PacketOut<Network::PktOut_C8> outMsg;
2✔
720
    outMsg->Write<u8>( update_range() );
2✔
721
    outMsg.Send( this );
2✔
722
    gd->original_client_update_range = Core::settingsManager.ssopt.default_visual_range;
2✔
723
  }
2✔
724
}
2✔
725

726
void Client::set_update_range_by_script( u8 range )
2✔
727
{
728
  if ( range == 0 )
2✔
729
  {
730
    range = gd->original_client_update_range ? gd->original_client_update_range
1✔
731
                                             : Core::settingsManager.ssopt.default_visual_range;
732
    gd->script_defined_update_range = false;
1✔
733
  }
734
  else
735
  {
736
    gd->script_defined_update_range = true;
1✔
737
  }
738
  // no limit check here, script is allowed to use different values
739
  set_update_range( range );
2✔
740
}
2✔
741

742
void Client::set_update_range( u8 range )
2✔
743
{
744
  auto old_range = update_range();
2✔
745
  // first signal the client about the change before we potentially send new objects otherwise the
746
  // client could directly drop them
747
  PktHelper::PacketOut<PktOut_C8> outMsg;
2✔
748
  outMsg->Write<u8>( range );
2✔
749
  outMsg.Send( this );
2✔
750

751
  // delete/send all objects, but not if its the first pkt
752
  if ( old_range != range && gd->original_client_update_range != 0 )
2✔
753
    chr->update_objects_on_range_change( range );
2✔
754

755
  gd->update_range = range;
2✔
756

757
  if ( old_range != range )
2✔
758
  {
759
    // update global updaterange (maximum multi radius/client view range)
760
    Core::gamestate.update_range_from_client( range );
2✔
761
  }
762
}
2✔
763

764
u8 Client::update_range() const
62,986✔
765
{
766
  return gd->update_range;
62,986✔
767
}
768

769
bool Client::acctSupports( Plib::ExpansionVersion v ) const
6,915✔
770
{
771
  return acct->expansion().hasExpansion( v );
6,915✔
772
}
773

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

800
// Threaded client stuff
801

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

829

830
}  // namespace Network
831
}  // 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