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

polserver / polserver / 12834126339

17 Jan 2025 05:43PM UTC coverage: 57.354% (+0.04%) from 57.311%
12834126339

push

github

web-flow
Suspend worldsave script (#745)

* SaveWorldState(): if possible the script will be suspended until the
async part is finished, added to the returning struct
`ElapsedMillisecondsTotal` for the complete time it takes.

* readded removed header

* extended test by calling save without beeing able to suspend

* fixed function name

* do not suspend script during worldsave when its critical
since testscripts are fast make sure via sleep that the gameclock
changes

* docs

47 of 56 new or added lines in 4 files covered. (83.93%)

1 existing line in 1 file now uncovered.

41124 of 71702 relevant lines covered (57.35%)

384088.06 hits per line

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

71.51
/pol-core/pol/pol.cpp
1
/** @file
2
 *
3
 * @par History
4
 * - 2005/02/23 Shinigami: ServSpecOpt DecayItems to enable/disable item decay
5
 * - 2005/04/03 Shinigami: send_feature_enable() call moved from start_client_char()
6
 *                         to send_start() to send before char selection
7
 * - 2005/04/04 Shinigami: added can_delete_character( chr, delete_by )
8
 * - 2005/06/15 Shinigami: ServSpecOpt UseWinLFH to enable/disable Windows XP/2003 low-fragmentation
9
 * Heap
10
 *                         added Check_for_WinXP_or_Win2003() and low_fragmentation_Heap()
11
 * - 2005/06/20 Shinigami: added llog (needs defined MEMORYLEAK)
12
 * - 2005/07/01 Shinigami: removed Check_for_WinXP_or_Win2003() and transformed call of
13
 *                         Use_low_fragmentation_Heap() into Run-Time Dynamic Linking
14
 * - 2005/10/13 Shinigami: added Check_libc_version() and printing a Warning if libc is to old
15
 * - 2005/11/25 Shinigami: PKTBI_BF::TYPE_SESPAM will not block Inactivity-Check
16
 * - 2005/11/28 MuadDib:   Created check_inactivity() bool function to handle checking packets
17
 *                         for ones to be considered "ignored" for inactivity. Returns true if
18
 *                         the packet was one to be ignored.
19
 * - 2005/11/28 MuadDib:   Implemented check_inactivity() function in appropriate place.
20
 * - 2006/03/01 MuadDib:   Added connect = true to start_client_char so char creation can use.
21
 * - 2006/03/03 MuadDib:   Moved all instances of connected = true to start_client_char.
22
 * - 2006/06/03 Shinigami: added little bit more logging @ pthread_create
23
 * - 2006/06/05 Shinigami: added little bit more logging @ Client disconnects by Core
24
 * - 2006/07/05 Shinigami: moved MakeDirectory("log") a little bit up
25
 * - 2006/10/07 Shinigami: FreeBSD fix - changed some __linux__ to __unix__
26
 * - 2007/03/08 Shinigami: added pthread_exit and _endhreadex to close threads
27
 * - 2007/05/06 Shinigami: smaller bugfix in Check_libc_version()
28
 * - 2007/06/17 Shinigami: Pergon-Linux-Release generates file "pol.pid"
29
 * - 2007/07/08 Shinigami: added UO:KR login process
30
 * - 2008/07/08 Turley:    removed Checkpoint "initializing random number generator"
31
 * - 2008/12/17 MuadDub:   Added check when loading Realms for no realms existing.
32
 * - 2009/01/19 Nando:     added unload_aux_services() and unload_packages() to the shutdown cleanup
33
 * - 2009/1/24  MuadDib:   Added read_bannedips_config() and checkpoint for it after loading of
34
 * pol.cfg
35
 * - 2009/07/23 MuadDib:   Updates for MSGOUT naming.
36
 * - 2009/07/31 MuadDib:   xmain_inner(): Force Client Disconnect to initiate cleanup of clients and
37
 * chars, after shutdown,
38
 *                         before other cleanups.
39
 * - 2009/08/01 MuadDib:   Removed send_tech_stuff(), send_betaclient_BF(), just_ignore_message(),
40
 * and ignore_69() due to not used or obsolete.
41
 * - 2009/08/03 MuadDib:   Renaming of MSG_HANDLER_6017 and related, to MSG_HANDLER_V2 for better
42
 * description
43
 *                         Renamed secondary handler class to *_V2 for naming convention
44
 * - 2009/08/14 Turley:    fixed definition of PKTIN_5D
45
 * - 2009/08/19 Turley:    PKTIN_5D clientflag saved in client->UOExpansionFlagClient
46
 * - 2009/09/03 MuadDib:   Relocation of account related cpp/h
47
 *                         Changes for multi related source file relocation
48
 * - 2009/09/15 MuadDib:   Multi registration/unregistration support added.
49
 * - 2009/09/06 Turley:    Changed Version checks to bitfield client->ClientType
50
 * - 2009/09/22 MuadDib:   Fix for lightlevel resets in client during login.
51
 * - 2009/11/19 Turley:    ssopt.core_sends_season & .core_handled_tags - Tomi
52
 * - 2009/12/04 Turley:    Crypto cleanup - Tomi
53
 * - 2010/01/22 Turley:    Speedhack Prevention System
54
 * - 2010/03/28 Shinigami: Transmit Pointer as Pointer and not Int as Pointer within
55
 * decay_thread_shadow
56
 * - 2011/11/12 Tomi:    Added extobj.cfg
57
 */
58

59
#include "pol.h"
60

61
#include <errno.h>
62

63
#include "pol_global_config.h"
64

65
#include "../bscript/bobject.h"
66
#include "../bscript/escriptv.h"
67
#include "../clib/Debugging/ExceptionParser.h"
68
#include "../clib/Program/ProgramConfig.h"
69
#include "../clib/clib_endian.h"
70
#include "../clib/esignal.h"
71
#include "../clib/fileutil.h"
72
#include "../clib/kbhit.h"
73
#include "../clib/logfacility.h"
74
#include "../clib/network/sockets.h"
75
#include "../clib/passert.h"
76
#include "../clib/rawtypes.h"
77
#include "../clib/refptr.h"
78
#include "../clib/stlutil.h"
79
#include "../clib/streamsaver.h"
80
#include "../clib/threadhelp.h"
81
#include "../clib/timer.h"
82
#include "../clib/tracebuf.h"
83
#include "../plib/pkg.h"
84
#include "../plib/systemstate.h"
85
#include "accounts/account.h"
86
#include "base/position.h"
87
#include "checkpnt.h"
88
#include "console.h"
89
#include "core.h"
90
#include "decay.h"
91
#include "extobj.h"
92
#include "fnsearch.h"
93
#include "gameclck.h"
94
#include "globals/network.h"
95
#include "globals/object_storage.h"
96
#include "globals/state.h"
97
#include "globals/uvars.h"
98
#include "item/armor.h"
99
#include "item/equipmnt.h"
100
#include "item/itemdesc.h"
101
#include "loadunld.h"
102
#include "menu.h"
103
#include "mobile/charactr.h"
104
#include "multi/house.h"
105
#include "multi/multi.h"
106
#include "network/cgdata.h"
107
#include "network/client.h"
108
#include "network/clientthread.h"
109
#include "network/clienttransmit.h"
110
#include "network/cliface.h"
111
#include "network/packethelper.h"
112
#include "network/packethooks.h"
113
#include "network/packets.h"
114
#include "network/pktboth.h"
115
#include "network/pktdef.h"
116
#include "network/pktin.h"
117
#include "network/sockio.h"
118
#include "party.h"
119
#include "polclock.h"
120
#include "poldbg.h"
121
#include "polsem.h"
122
#include "polsig.h"
123
#include "polwww.h"
124
#include "profile.h"
125
#include "realms/WorldChangeReasons.h"
126
#include "realms/realm.h"
127
#include "regions/guardrgn.h"
128
#include "regions/miscrgn.h"
129
#include "regions/musicrgn.h"
130
#include "savedata.h"
131
#include "scrdef.h"
132
#include "scrsched.h"
133
#include "sqlscrobj.h"
134
#include "ssopt.h"
135
#include "testing/poltest.h"
136
#include "ufunc.h"
137
#include "uimport.h"
138
#include "uoclient.h"
139
#include "uoscrobj.h"
140
#include "uworld.h"
141

142

143
#ifndef NDEBUG
144
#include "containr.h"
145
#include "mobile/npc.h"
146
#endif
147

148
#ifdef _WIN32
149
#include <process.h>
150

151
#include "../clib/mdump.h"
152
#endif
153

154
#ifdef __linux__
155
#include <gnu/libc-version.h>
156
#endif
157

158

159
#include <cstdio>
160
#include <cstring>
161
#include <exception>
162
#include <iosfwd>
163
#include <string>
164

165
#ifdef _MSC_VER
166
#pragma warning( disable : 4127 )  // conditional expression is constant (needed because of FD_SET)
167
#endif
168

169
namespace Pol
170
{
171
namespace Bscript
172
{
173
void display_executor_instances();
174
void display_bobjectimp_instances();
175
}  // namespace Bscript
176
namespace Items
177
{
178
void load_intrinsic_weapons();
179
void allocate_intrinsic_weapon_serials();
180
}  // namespace Items
181
namespace Network
182
{
183
void load_aux_services();
184
void start_aux_services();
185
void read_bannedips_config( bool initial_load );
186
}  // namespace Network
187
namespace Core
188
{
189
void cancel_all_trades();
190
void load_system_hooks();
191
bool load_realms();
192
void InitializeSystemTrayHandling();
193
void ShutdownSystemTrayHandling();
194
void start_uo_client_listeners( void );
195
void start_tasks();
196

197

198
using namespace threadhelp;
199

200
void send_startup( Network::Client* client )
2✔
201
{
202
  Mobile::Character* chr = client->chr;
2✔
203
  Network::PktHelper::PacketOut<Network::PktOut_1B> msg;
2✔
204
  msg->Write<u32>( chr->serial_ext );
2✔
205
  msg->offset += 4;  // u8 unk5, unk6, unk7, unk8
2✔
206
  msg->WriteFlipped<u16>( chr->graphic );
2✔
207
  msg->WriteFlipped<u16>( chr->x() );
2✔
208
  msg->WriteFlipped<u16>( chr->y() );
2✔
209
  msg->offset++;  // u8 unk_15
2✔
210
  msg->Write<s8>( chr->z() );
2✔
211
  msg->Write<u8>( chr->facing );
2✔
212
  msg->offset += 3;  // u8 unk18,unk19,unk20
2✔
213
  msg->Write<u8>( 0x7Fu );
2✔
214
  msg->offset++;     // u8 unk22
2✔
215
  msg->offset += 4;  // u16 map_startx, map_starty
2✔
216
  msg->WriteFlipped<u16>( client->chr->realm()->width() );
2✔
217
  msg->WriteFlipped<u16>( client->chr->realm()->height() );
2✔
218
  msg->offset += 6;  // u8 unk31, unk32, unk33, unk34, unk35, unk36
2✔
219
  msg.Send( client );
2✔
220
}
2✔
221

222
void send_inrange_items( Network::Client* client )
×
223
{
224
  WorldIterator<ItemFilter>::InMaxVisualRange( client->chr,
×
225
                                               [&]( Items::Item* item )
×
226
                                               {
227
                                                 if ( client->chr->in_visual_range( item ) )
×
228
                                                   send_item( client, item );
×
229
                                               } );
×
230
}
×
231

232
void send_inrange_multis( Network::Client* client )
×
233
{
234
  WorldIterator<MultiFilter>::InMaxVisualRange( client->chr,
×
235
                                                [&]( Multi::UMulti* multi )
×
236
                                                {
237
                                                  if ( client->chr->in_visual_range( multi ) )
×
238
                                                    send_multi( client, multi );
×
239
                                                } );
×
240
}
×
241

242
void textcmd_startlog( Network::Client* client );
243
void textcmd_stoplog( Network::Client* client );
244
void start_client_char( Network::Client* client )
2✔
245
{
246
  client->ready = true;
2✔
247
  client->chr->connected( true );
2✔
248

249
  // even if this stuff just gets queued, we still want the client to start
250
  // getting data now.
251
  client->pause();
2✔
252

253
  Multi::UMulti* supporting_multi;
254
  Items::Item* walkon;
255
  short newz;
256
  if ( client->chr->realm()->walkheight( client->chr, client->chr->pos2d(), client->chr->z(), &newz,
2✔
257
                                         &supporting_multi, &walkon ) )
258
  {
259
    client->chr->setposition( Pos4d( client->chr->pos() ).z( static_cast<s8>( newz ) ) );
×
260
    if ( supporting_multi != nullptr )
×
261
    {
262
      supporting_multi->register_object( client->chr );
×
263
      if ( client->chr->registered_multi == 0 )
×
264
      {
265
        client->chr->registered_multi = supporting_multi->serial;
×
266
        supporting_multi->walk_on( client->chr );
×
267
      }
268
    }
269
    else
270
    {
271
      if ( client->chr->registered_multi > 0 )
×
272
      {
273
        Multi::UMulti* multi = system_find_multi( client->chr->registered_multi );
×
274
        if ( multi != nullptr )
×
275
        {
276
          multi->unregister_object( client->chr );
×
277
        }
278
        client->chr->registered_multi = 0;
×
279
      }
280
    }
281
    client->chr->position_changed();
×
282
  }
283

284
  send_startup( client );
2✔
285

286
  send_realm_change( client, client->chr->realm() );
2✔
287
  send_map_difs( client );
2✔
288

289
  if ( settingsManager.ssopt.core_sends_season )
2✔
290
    send_season_info( client );
2✔
291

292
  client->chr->lastpos = Pos4d( 0, 0, 0, nullptr );
2✔
293

294
  client->gd->music_region =
4✔
295
      gamestate.musicdef->getregion( Pos4d( 0, 0, 0, client->chr->realm() ) );
2✔
296
  client->gd->justice_region =
4✔
297
      gamestate.justicedef->getregion( Pos4d( 0, 0, 0, client->chr->realm() ) );
2✔
298
  client->gd->weather_region =
4✔
299
      gamestate.weatherdef->getregion( Pos4d( 0, 0, 0, client->chr->realm() ) );
2✔
300

301
  send_goxyz( client, client->chr );
2✔
302
  client->chr->check_region_changes();
2✔
303

304
  client->chr->send_warmode();
2✔
305
  login_complete( client );
2✔
306
  client->chr->tellmove();
2✔
307

308
  client->chr->check_weather_region_change( true );
2✔
309

310
  if ( settingsManager.ssopt.core_sends_season )
2✔
311
    send_season_info( client );
2✔
312

313
  send_objects_newly_inrange( client );
2✔
314

315
  client->chr->send_highlight();
2✔
316
  send_owncreate( client, client->chr );
2✔
317

318
  send_goxyz( client, client->chr );
2✔
319

320
  client->restart();
2✔
321

322
  client->chr->clear_gotten_item();
2✔
323
  on_loggon_party( client->chr );
2✔
324
  client->chr->send_buffs();
2✔
325

326

327
  //  Moved login_complete higher to prevent weather regions from messing up client
328
  //  spell icon packets(made it look like it was raining spell icons from spellbook if logged
329
  //  into a weather region with rain.
330
  //  login_complete(client);
331
}
2✔
332

333

334
void call_chr_scripts( Mobile::Character* chr, const std::string& root_script_ecl,
7✔
335
                       const std::string& pkg_script_ecl, bool offline = false )
336
{
337
  ScriptDef sd;
7✔
338
  sd.quickconfig( root_script_ecl );
7✔
339

340
  if ( sd.exists() )
7✔
341
  {
342
    call_script( sd, offline ? new Module::EOfflineCharacterRefObjImp( chr )
×
343
                             : new Module::ECharacterRefObjImp( chr ) );
×
344
  }
345

346
  for ( Plib::Packages::iterator itr = Plib::systemstate.packages.begin();
7✔
347
        itr != Plib::systemstate.packages.end(); ++itr )
133✔
348
  {
349
    Plib::Package* pkg = *itr;
126✔
350

351
    sd.quickconfig( pkg, pkg_script_ecl );
126✔
352
    if ( sd.exists() )
126✔
353
    {
354
      call_script( sd, offline ? new Module::EOfflineCharacterRefObjImp( chr )
×
355
                               : new Module::ECharacterRefObjImp( chr ) );
×
356
    }
357
  }
358
}
7✔
359

360
void run_logon_script( Mobile::Character* chr )
2✔
361
{
362
  call_chr_scripts( chr, "scripts/misc/logon.ecl", "logon.ecl" );
2✔
363
}
2✔
364
void run_reconnect_script( Mobile::Character* chr )
×
365
{
366
  call_chr_scripts( chr, "scripts/misc/reconnect.ecl", "reconnect.ecl" );
×
367
}
×
368
bool can_delete_character( Mobile::Character* chr, int delete_by )
3✔
369
{
370
  ScriptDef sd;
3✔
371
  sd.quickconfig( "scripts/misc/candelete.ecl" );
3✔
372

373
  if ( sd.exists() )
3✔
374
  {
375
    return call_script( sd, new Module::EOfflineCharacterRefObjImp( chr ),
×
376
                        new Bscript::BLong( delete_by ) );
×
377
  }
378
  else
379
  {
380
    return true;
3✔
381
  }
382
}
3✔
383
void call_ondelete_scripts( Mobile::Character* chr )
3✔
384
{
385
  call_chr_scripts( chr, "scripts/misc/ondelete.ecl", "ondelete.ecl", true );
3✔
386
}
3✔
387

388
// FIXME: Consider moving most of this into a function, so character
389
// creation can use the same code.
390
void char_select( Network::Client* client, PKTIN_5D* msg )
2✔
391
{
392
  bool reconnecting = false;
2✔
393
  int charidx = cfBEu32( msg->charidx );
2✔
394
  if ( ( charidx >= Plib::systemstate.config.character_slots ) || ( client->acct == nullptr ) ||
4✔
395
       ( client->acct->get_character( charidx ) == nullptr ) )
2✔
396
  {
397
    send_login_error( client, LOGIN_ERROR_MISC );
×
398
    client->Disconnect();
×
399
    return;
×
400
  }
401

402
  Mobile::Character* chosen_char = client->acct->get_character( charidx );
2✔
403

404
  POLLOGLN( "Account {} selecting character {}", client->acct->name(), chosen_char->name() );
2✔
405

406
  if ( Plib::systemstate.config.min_cmdlevel_to_login > chosen_char->cmdlevel() )
2✔
407
  {
408
    POLLOGLN(
×
409
        "Account {} with character {} doesn't fit MinCmdlevelToLogin from pol.cfg. Client "
410
        "disconnected by Core.",
411
        client->acct->name(), chosen_char->name() );
×
412

413
    send_login_error( client, LOGIN_ERROR_MISC );
×
414
    client->Disconnect();
×
415
    return;
×
416
  }
417

418
  // Dave moved this from login.cpp so client cmdlevel can be checked before denying login
419
  if ( ( ( std::count_if( networkManager.clients.begin(), networkManager.clients.end(),
2✔
420
                          clientHasCharacter ) ) >= Plib::systemstate.config.max_clients ) &&
2✔
421
       ( chosen_char->cmdlevel() < Plib::systemstate.config.max_clients_bypass_cmdlevel ) )
×
422
  {
423
    POLLOGLN(
×
424
        "To much clients connected. Check MaximumClients and/or MaximumClientsBypassCmdLevel in "
425
        "pol.cfg.\n"
426
        "Account {} with character {} Client disconnected by Core.",
427
        client->acct->name(), chosen_char->name() );
×
428

429
    send_login_error( client, LOGIN_ERROR_MISC );
×
430
    client->Disconnect();
×
431
    return;
×
432
  }
433

434
  if ( chosen_char->client )
2✔
435
  {
436
    // we're reattaching to a character that is in-game.  If there is still
437
    // a client attached, disconnect it.
438

439
    chosen_char->client->gd->clear();
×
440
    chosen_char->client->forceDisconnect();
×
441
    chosen_char->client->ready = false;
×
442
    chosen_char->client->msgtype_filter = networkManager.disconnected_filter.get();
×
443

444
    // disassociate the objects from each other.
445
    chosen_char->client->acct = nullptr;
×
446
    chosen_char->client->chr = nullptr;
×
447

448
    chosen_char->client = nullptr;
×
449
    reconnecting = true;
×
450
  }
451
  else if ( !Plib::systemstate.config.allow_multi_clients_per_account &&
4✔
452
            client->acct->has_active_characters() )
2✔
453
  {
454
    // We are trying to attach a new character, but AllowMultiCharacters is not set
455
    send_login_error( client, LOGIN_ERROR_OTHER_CHAR_INUSE );
×
456
    client->Disconnect();
×
457
    return;
×
458
  }
459
  else
460
  {
461
    // logging in a character that's offline.
462
    SetCharacterWorldPosition( chosen_char, Realms::WorldChangeReason::PlayerEnter );
2✔
463
    chosen_char->logged_in( true );
2✔
464
  }
465

466
  client->chr = chosen_char;
2✔
467
  chosen_char->client = client;
2✔
468
  chosen_char->acct.set( client->acct );
2✔
469

470
  client->UOExpansionFlagClient = cfBEu32( msg->clientflags );
2✔
471

472
  client->msgtype_filter = networkManager.game_filter.get();
2✔
473
  start_client_char( client );
2✔
474

475
  if ( !chosen_char->lastpos.realm() )
2✔
476
    chosen_char->lastpos = chosen_char->pos();
2✔
477

478
  if ( !reconnecting )
2✔
479
    run_logon_script( chosen_char );
2✔
480
  else
481
    run_reconnect_script( chosen_char );
×
482
}
483

484
void send_client_char_data( Mobile::Character* chr, Network::Client* client );
485
void handle_resync_request( Network::Client* client, PKTBI_22_SYNC* /*msg*/ )
×
486
{
487
  send_goxyz( client, client->chr );
×
488

489
  client->send_pause();  // dave removed force=true 5/10/3, let uoclient.cfg option determine xflow
×
490
                         // packets (else this hangs 4.0.0e clients)
491

492
  Core::WorldIterator<Core::MobileFilter>::InRange( client->chr->pos(), client->chr->los_size(),
×
493
                                                    [&]( Mobile::Character* zonechr )
×
494
                                                    { send_client_char_data( zonechr, client ); } );
×
495

496
  send_inrange_items( client );
×
497
  send_inrange_multis( client );
×
498

499
  client->send_restart();  // dave removed force=true 5/10/3
×
500
}
×
501

502
void restart_all_clients()
59,403,651✔
503
{
504
  if ( !networkManager.uoclient_protocol.EnableFlowControlPackets )
59,403,651✔
505
    return;
59,403,651✔
506
  for ( Clients::iterator itr = networkManager.clients.begin(), end = networkManager.clients.end();
×
507
        itr != end; ++itr )
×
508
  {
509
    Network::Client* client = ( *itr );
×
510
    if ( client->pause_count )
×
511
    {
512
      client->restart();
×
513
    }
514
  }
515
}
516

517
void polclock_checkin()
59,403,591✔
518
{
519
  stateManager.checkin_clock_times_out_at = polclock() + 30 * POLCLOCKS_PER_SEC;
59,403,591✔
520
}
59,403,591✔
521

522
#define clock_t_to_ms( x ) ( x )
523

524
void tasks_thread( void )
2✔
525
{
526
  polclock_t sleeptime;
527
  bool activity;
528
  try
529
  {
530
    while ( !Clib::exit_signalled )
86✔
531
    {
532
      THREAD_CHECKPOINT( tasks, 1 );
84✔
533
      {
534
        PolLock lck;
84✔
535
        polclock_checkin();
84✔
536
        THREAD_CHECKPOINT( tasks, 2 );
84✔
537
        INC_PROFILEVAR( scheduler_passes );
84✔
538
        check_scheduled_tasks( &sleeptime, &activity );
84✔
539
        THREAD_CHECKPOINT( tasks, 3 );
84✔
540
        restart_all_clients();
84✔
541
        THREAD_CHECKPOINT( tasks, 5 );
84✔
542
      }
84✔
543

544
      THREAD_CHECKPOINT( tasks, 6 );
84✔
545
      if ( activity )
84✔
546
        send_pulse();
72✔
547
      else
548
        INC_PROFILEVAR( noactivity_scheduler_passes );
12✔
549
      THREAD_CHECKPOINT( tasks, 7 );
84✔
550

551
      passert( sleeptime > 0 );
84✔
552

553
      TRACEBUF_ADDELEM( "tasks wait_for_pulse now", static_cast<u32>( polclock() ) );
554
      TRACEBUF_ADDELEM( "tasks wait_for_pulse sleeptime", static_cast<u32>( sleeptime ) );
555

556
      THREAD_CHECKPOINT( tasks, 8 );
84✔
557
      tasks_thread_sleep( static_cast<u32>( polclock_t_to_ms( sleeptime ) ) );
84✔
558
      THREAD_CHECKPOINT( tasks, 9 );
84✔
559
    }
560
  }
561
  catch ( const char* msg )
×
562
  {
563
    POLLOGLN( "Tasks Thread exits due to exception: {}", msg );
×
564
    throw;
×
565
  }
×
566
  catch ( std::string& str )
×
567
  {
568
    POLLOGLN( "Tasks Thread exits due to exception: {}", str );
×
569
    throw;
×
570
  }
×
571
  catch ( std::exception& ex )
×
572
  {
573
    POLLOGLN( "Tasks Thread exits due to exception: {}", ex.what() );
×
574
    throw;
×
575
  }
×
576
}
2✔
577

578
void scripts_thread( void )
2✔
579
{
580
  polclock_t sleeptime;
581
  bool activity;
582
  while ( !Clib::exit_signalled )
59,403,348✔
583
  {
584
    THREAD_CHECKPOINT( scripts, 0 );
59,403,344✔
585
    {
586
      PolLock lck;
59,403,344✔
587
      polclock_checkin();
59,403,344✔
588
      TRACEBUF_ADDELEM( "scripts thread now", static_cast<u32>( polclock() ) );
589
      ++stateManager.profilevars.script_passes;
59,403,344✔
590
      THREAD_CHECKPOINT( scripts, 1 );
59,403,344✔
591

592
      step_scripts( &sleeptime, &activity );
59,403,344✔
593

594
      THREAD_CHECKPOINT( scripts, 50 );
59,403,344✔
595

596
      restart_all_clients();
59,403,344✔
597

598
      THREAD_CHECKPOINT( scripts, 52 );
59,403,344✔
599

600
      if ( TaskScheduler::is_dirty() )
59,403,344✔
601
      {
602
        THREAD_CHECKPOINT( scripts, 53 );
400✔
603

604
        wake_tasks_thread();
400✔
605
      }
606
    }
59,403,344✔
607

608
    if ( activity )
59,403,344✔
609
    {
610
      ++stateManager.profilevars.script_passes_activity;
59,403,343✔
611
    }
612
    else
613
    {
614
      ++stateManager.profilevars.script_passes_noactivity;
1✔
615
    }
616

617
    if ( sleeptime )
59,403,344✔
618
    {
619
      THREAD_CHECKPOINT( scripts, 54 );
1✔
620

621
      wait_for_pulse( static_cast<u32>( polclock_t_to_ms( sleeptime ) ) );
1✔
622

623
      THREAD_CHECKPOINT( scripts, 55 );
1✔
624
    }
625
  }
626
}
2✔
627

628
template <class T>
629
inline void Delete( T* p )
630
{
631
  delete p;
632
}
633

634
template <class T>
635
class delete_ob
636
{
637
public:
638
  void operator()( T* p ) { delete p; }
639
};
640

641
void reap_thread( void )
2✔
642
{
643
  while ( !Clib::exit_signalled )
35✔
644
  {
645
    {
646
      PolLock lck;
33✔
647
      polclock_checkin();
33✔
648
      objStorageManager.objecthash.Reap();
33✔
649
      for ( auto& item : gamestate.dynamic_item_descriptors )
36✔
650
      {
651
        delete item;
3✔
652
      }
653
      gamestate.dynamic_item_descriptors.clear();
33✔
654
    }
33✔
655

656
    threadhelp::thread_sleep_ms( 2000 );
33✔
657
  }
658
}
2✔
659

660

661
void threadstatus_thread( void )
2✔
662
{
663
  int timeouts_remaining = 1;
2✔
664
  bool sent_wakeups = false;
2✔
665
  // we want this thread to be the last out, so that it can report stuff at shutdown.
666
  while ( !Clib::exit_signalled || threadhelp::child_threads > 1 )
75✔
667
  {
668
    if ( is_polclock_paused_at_zero() )
73✔
669
    {
670
      polclock_t now = polclock();
73✔
671
      if ( now >= stateManager.checkin_clock_times_out_at )
73✔
672
      {
673
        ERROR_PRINTLN(
×
674
            "########################################################\n"
675
            "No clock movement in 30 seconds.  Dumping thread status." );
676
        stateManager.polsig.report_status_signalled = true;
×
677
        stateManager.checkin_clock_times_out_at = now + 30 * POLCLOCKS_PER_SEC;
×
678
      }
679
    }
680

681
    if ( stateManager.polsig.report_status_signalled )
73✔
682
    {
683
      std::string tmp = fmt::format(
684
          "*Thread Info*\n"
685
          "Semaphore TID: {}\n",
686
          locker );
×
687

688
      if ( Plib::systemstate.config.log_traces_when_stuck )
×
689
        Pol::Clib::ExceptionParser::logAllStackTraces();
×
690

691
      fmt::format_to( std::back_inserter( tmp ),
×
692
                      "Scripts Thread Checkpoint: {}\n"
693
                      "Last Script: {} PC: {}\n"
694
                      "Escript Instruction Cycles: {}\n"
695
                      "Tasks Thread Checkpoint: {}\n"
696
                      "Active Client Thread Checkpoint: {}\n"
697
                      "Number of clients: {}\n",
698
                      stateManager.polsig.scripts_thread_checkpoint, Clib::scripts_thread_script,
699
                      Clib::scripts_thread_scriptPC, Bscript::escript_instr_cycles,
700
                      stateManager.polsig.tasks_thread_checkpoint,
701
                      stateManager.polsig.active_client_thread_checkpoint,
702
                      Core::networkManager.clients.size() );
×
703
      for ( const auto& client : Core::networkManager.clients )
×
704
        fmt::format_to( std::back_inserter( tmp ), " {} {} {}\n", client->ipaddrAsString(),
×
705
                        client->acct == nullptr ? "prelogin " : client->acct->name(),
×
706
                        client->session()->checkpoint );
×
707
      if ( stateManager.polsig.check_attack_after_move_function_checkpoint )
×
708
        fmt::format_to( std::back_inserter( tmp ), "check_attack_after_move() Checkpoint: {}\n",
×
709
                        stateManager.polsig.check_attack_after_move_function_checkpoint );
710
      tmp += "Current Threads:\n";
×
711
      ThreadMap::Contents contents;
×
712
      threadmap.CopyContents( contents );
×
713
      for ( ThreadMap::Contents::const_iterator citr = contents.begin(); citr != contents.end();
×
714
            ++citr )
×
715
      {
716
        fmt::format_to( std::back_inserter( tmp ), "{} - {}\n", ( *citr ).first, ( *citr ).second );
×
717
      }
718
      fmt::format_to( std::back_inserter( tmp ),
×
719
                      "Child threads (child_threads): {}\n"
720
                      "Registered threads (ThreadMap): {}",
721
                      threadhelp::child_threads, contents.size() );
×
722
      stateManager.polsig.report_status_signalled = false;
×
723
      ERROR_PRINTLN( tmp );
×
724
    }
×
725
    if ( Clib::exit_signalled )
73✔
726
    {
727
      if ( !sent_wakeups )
8✔
728
      {
729
        send_pulse();
2✔
730
        wake_tasks_thread();
2✔
731
        networkManager.clientTransmit->Cancel();
2✔
732
#ifdef HAVE_MYSQL
733
        networkManager.sql_service->stop();
2✔
734
#endif
735
        sent_wakeups = true;
2✔
736
      }
737

738
      --timeouts_remaining;
8✔
739
      if ( timeouts_remaining == 0 )
8✔
740
      {
741
        INFO_PRINTLN( "Waiting for {} child threads to exit", threadhelp::child_threads );
2✔
742
        timeouts_remaining = 5;
2✔
743
      }
744
    }
745
    pol_sleep_ms( 1000 );
73✔
746
  }
747
  // cerr << "threadstatus thread exits." << endl;
748
  signal_catch_thread();
2✔
749
}
2✔
750

751
void catch_signals_thread( void );
752
void reload_configuration();
753

754
void console_thread( void )
2✔
755
{
756
#ifndef _WIN32
757
  Clib::KeyboardHook kb;  // local to have a defined deconstruction to uninstall the hook
2✔
758
#endif
759
  while ( !Clib::exit_signalled )
21✔
760
  {
761
    pol_sleep_ms( 1000 );
17✔
762
#ifdef _WIN32
763
    ConsoleCommand::check_console_commands();
764
#else
765
    ConsoleCommand::check_console_commands( &kb );
17✔
766
    if ( stateManager.polsig.reload_configuration_signalled )
17✔
767
    {
768
      PolLock lck;
×
769
      INFO_PRINT( "Reloading configuration..." );
×
770
      stateManager.polsig.reload_configuration_signalled = false;
×
771
      reload_configuration();
×
772
      INFO_PRINTLN( "Done." );
×
773
    }
×
774
#endif
775
  }
776
}
2✔
777

778
void start_threads()
2✔
779
{
780
  threadmap.Register( thread_pid(), "Main" );
2✔
781

782
  if ( Plib::systemstate.config.web_server )
2✔
783
    start_http_server();
2✔
784

785
  checkpoint( "start tasks thread" );
2✔
786
  threadhelp::start_thread( tasks_thread, "Tasks" );
2✔
787
  checkpoint( "start scripts thread" );
2✔
788
  threadhelp::start_thread( scripts_thread, "Scripts" );
2✔
789

790
  if ( settingsManager.ssopt.decay_items )
2✔
791
  {
792
    checkpoint( "start decay thread" );
2✔
793
    threadhelp::start_thread( Decay::decay_thread, "Decay", nullptr );
2✔
794
  }
795
  else
796
  {
797
    checkpoint( "don't start decay thread" );
×
798
  }
799

800
  checkpoint( "start reap thread" );
2✔
801
  threadhelp::start_thread( reap_thread, "Reap" );
2✔
802

803
  checkpoint( "start dbglisten thread" );
2✔
804
  threadhelp::start_thread( debug_listen_thread, "DbgListn" );
2✔
805

806
  checkpoint( "start threadstatus thread" );
2✔
807
  start_thread( threadstatus_thread, "ThreadStatus" );
2✔
808

809
  checkpoint( "start clienttransmit thread" );
2✔
810
  start_thread( Network::ClientTransmitThread, "ClientTransmit" );
2✔
811

812
#ifdef HAVE_MYSQL
813
  checkpoint( "start sql service thread" );
2✔
814
  start_sql_service();
2✔
815
#endif
816
}
2✔
817

818
#if REFPTR_DEBUG
819
unsigned int ref_counted::_ctor_calls;
820
#endif
821

822
void display_unreaped_orphan_instances();
823
void display_reftypes();
824

825
void display_leftover_objects()
3✔
826
{
827
  Bscript::display_executor_instances();
3✔
828
  display_unreaped_orphan_instances();
3✔
829
#if BOBJECTIMP_DEBUG
830
  Bscript::display_bobjectimp_instances();
831
#endif
832
  display_reftypes();
3✔
833
  {
834
    std::ofstream ofs;
3✔
835
    ofs.open( "leftovers.txt", std::ios::out | std::ios::trunc );
3✔
836
    objStorageManager.objecthash.PrintContents( &ofs );
3✔
837
  }
3✔
838
  std::string tmp;
3✔
839
  if ( stateManager.uobjcount.uobject_count != 0 )
3✔
840
    tmp += fmt::format( "Remaining UObjects: {}\n", stateManager.uobjcount.uobject_count );
×
841
  if ( stateManager.uobjcount.ucharacter_count != 0 )
3✔
842
    tmp += fmt::format( "Remaining Mobiles: {}\n", stateManager.uobjcount.ucharacter_count );
×
843
  if ( stateManager.uobjcount.npc_count != 0 )
3✔
844
    tmp += fmt::format( "Remaining NPCs: {}\n", stateManager.uobjcount.npc_count );
×
845
  if ( stateManager.uobjcount.uitem_count != 0 )
3✔
846
    tmp += fmt::format( "Remaining Items: {}\n", stateManager.uobjcount.uitem_count );
×
847
  if ( stateManager.uobjcount.umulti_count != 0 )
3✔
848
    tmp += fmt::format( "Remaining Multis: {}\n", stateManager.uobjcount.umulti_count );
×
849
  if ( stateManager.uobjcount.unreaped_orphans != 0 )
3✔
850
    tmp += fmt::format( "Unreaped orphans: {}\n", stateManager.uobjcount.unreaped_orphans );
×
851
  if ( stateManager.uobjcount.uobj_count_echrref != 0 )
3✔
852
    tmp +=
853
        fmt::format( "Remaining EChrRef objects: {}\n", stateManager.uobjcount.uobj_count_echrref );
×
854
  if ( Bscript::executor_count )
3✔
855
    tmp += fmt::format( "Remaining Executors: {}\n", Bscript::executor_count );
×
856
  if ( Bscript::eobject_imp_count )
3✔
857
    tmp += fmt::format( "Remaining script objectimps: {}\n", Bscript::eobject_imp_count );
×
858
  INFO_PRINT( tmp );
3✔
859
}
3✔
860

861
void run_start_scripts()
2✔
862
{
863
  INFO_PRINTLN( "Running startup script." );
2✔
864
  run_script_to_completion( "start" );
2✔
865
  for ( const auto& pkg : Plib::systemstate.packages )
38✔
866
  {
867
    std::string scriptname = pkg->dir() + "start.ecl";
36✔
868

869
    if ( Clib::FileExists( scriptname.c_str() ) )
36✔
870
    {
871
      ScriptDef script( "start", pkg, "" );
×
872
      Bscript::BObject obj( run_script_to_completion( script ) );
×
873
    }
×
874
  }
36✔
875
  INFO_PRINTLN( "Startup script complete." );
2✔
876
}
2✔
877

878
#ifdef _WIN32
879
typedef BOOL( WINAPI* DynHeapSetInformation )( PVOID HeapHandle,
880
                                               HEAP_INFORMATION_CLASS HeapInformationClass,
881
                                               PVOID HeapInformation,
882
                                               SIZE_T HeapInformationLength );
883

884
const char* Use_low_fragmentation_Heap()
885
{
886
  if ( settingsManager.ssopt.use_win_lfh )
887
  {
888
    HINSTANCE hKernel32;
889

890
    hKernel32 = LoadLibrary( "Kernel32" );
891
    if ( hKernel32 != nullptr )
892
    {
893
      DynHeapSetInformation ProcAdd;
894
      ProcAdd = (DynHeapSetInformation)GetProcAddress( hKernel32, "HeapSetInformation" );
895
      if ( ProcAdd != nullptr )
896
      {
897
        ULONG HeapFragValue = 2;
898

899
        if ( (ProcAdd)( GetProcessHeap(), HeapCompatibilityInformation, &HeapFragValue,
900
                        sizeof( HeapFragValue ) ) )
901
        {
902
          FreeLibrary( hKernel32 );
903
          return "low-fragmentation Heap ...activated";
904
        }
905
        else
906
        {
907
          FreeLibrary( hKernel32 );
908
          return "low-fragmentation Heap ...not activated";
909
        }
910
      }
911
      else
912
      {
913
        FreeLibrary( hKernel32 );
914
        return "low-fragmentation Heap ...not available on your Windows";
915
      }
916
    }
917
    else
918
      return "low-fragmentation Heap ...not available on your Windows";
919
  }
920
  else
921
    return "low-fragmentation Heap ...disabled via ServSpecOpt";
922
}
923
#endif
924

925
#ifdef __linux__
926
void Check_libc_version()
3✔
927
{
928
  const char* libc_version = gnu_get_libc_version();
3✔
929

930
  int main_version = 0;
3✔
931
  int sub_version = 0;
3✔
932
  int build = 0;
3✔
933
  ISTRINGSTREAM is( libc_version );
3✔
934

935
  if ( is >> main_version )
3✔
936
  {
937
    char delimiter;
938
    if ( is >> delimiter >> sub_version )
3✔
939
    {
940
      is >> delimiter >> build;
3✔
941
    }
942
  }
943
  else
944
    POLLOG_ERRORLN( "Error in analyzing libc version string [{}]. Please contact Core-Team.",
×
945
                    libc_version );
946

947
  if ( main_version * 100000000 + sub_version * 10000 + build >= 2 * 100000000 + 3 * 10000 + 2 )
3✔
948
    POLLOG_INFOLN( "Found libc {} - ok", libc_version );
3✔
949
  else
950
    POLLOG_ERRORLN( "Found libc {} - Please update to 2.3.2 or above.", libc_version );
×
951
}
3✔
952
#endif
953

954

955
}  // namespace Core
956

957
int xmain_inner( bool testing )
3✔
958
{
959
#ifdef _WIN32
960
  Clib::MiniDumper::Initialize();
961
  // Aug. 15, 2006 Austin
962
  // Added atexit() call to remove the tray icon.
963
  atexit( Core::ShutdownSystemTrayHandling );
964
#else
965
#ifdef __linux__
966
  std::ofstream polpid;
3✔
967

968
  polpid.open( ( Plib::systemstate.config.pidfile_path + "pol.pid" ).c_str(),
3✔
969
               std::ios::out | std::ios::trunc );
970

971
  if ( polpid.is_open() )
3✔
972
    polpid << Clib::tostring( getpid() );
3✔
973
  else
974
    INFO_PRINTLN( "Cannot create pid file in {}", Plib::systemstate.config.pidfile_path );
×
975

976
  polpid.close();
3✔
977
#endif
978
#endif
979

980
  // problem with order of global construction, threads cannot be registered in the constructor of
981
  // gamestate :(
982
  Core::gamestate.task_thread_pool.init_pool(
9✔
983
      std::max( 2u, std::thread::hardware_concurrency() / 2 ), "generic_task_thread" );
6✔
984

985
  int res;
986

987
  // for profiling:
988
  // chdir( "d:\\pol" );
989
  // PrintAllocationData();
990

991
  Clib::MakeDirectory( "log" );
3✔
992

993
  POLLOG_INFOLN( "POL {} - {}\nCompiled on {}\n{}", POL_VERSION_ID,
3✔
994
                 Clib::ProgramConfig::build_target(), Clib::ProgramConfig::build_datetime(),
6✔
995
                 POL_COPYRIGHT );
996
  if ( testing )
3✔
997
    POLLOG_INFOLN( "TESTING MODE" );
1✔
998

999
#ifndef NDEBUG
1000
  POLLOG_INFOLN(
1001
      "Sizes: \n"
1002
      "   UObject:    {}\n"
1003
      "   Item:       {}\n"
1004
      "   UContainer: {}\n"
1005
      "   Character:  {}\n"
1006
      "   Client:     {}\n"
1007
      "   NPC:        {}",
1008
      sizeof( Core::UObject ), sizeof( Items::Item ), sizeof( Core::UContainer ),
1009
      sizeof( Mobile::Character ), sizeof( Network::Client ), sizeof( Mobile::NPC ) );
1010

1011
#ifdef __unix__
1012
#ifdef PTHREAD_THREADS_MAX
1013
  POLLOG_INFOLN( "   Max Threads: {}", PTHREAD_THREADS_MAX );
1014
#endif
1015
#endif
1016
  POLLOG_INFOLN( "" );
1017
#endif
1018
  POLLOG_INFOLN( "Using {} out of {} worldsave threads", Core::gamestate.task_thread_pool.size(),
3✔
1019
                 std::thread::hardware_concurrency() );
3✔
1020

1021
  Core::checkpoint( "installing signal handlers" );
3✔
1022
  Core::install_signal_handlers();
3✔
1023

1024
  Core::checkpoint( "starting POL clocks" );
3✔
1025
  Core::start_pol_clocks();
3✔
1026
  Core::pause_pol_clocks();
3✔
1027

1028
  POLLOG_INFOLN( "Reading Configuration." );
3✔
1029

1030
  Core::stateManager.gflag_in_system_startup = true;
3✔
1031

1032
  Core::checkpoint( "reading pol.cfg" );
3✔
1033
  Plib::systemstate.config.read( true );
3✔
1034
  Core::apply_polcfg( true );
3✔
1035

1036
  Core::checkpoint( "reading config/bannedips.cfg" );
3✔
1037
  Network::read_bannedips_config( true );
3✔
1038

1039
  Core::checkpoint( "reading servspecopt.cfg" );
3✔
1040
  Core::ServSpecOpt::read_servspecopt();
3✔
1041

1042
  Core::checkpoint( "reading extobj.cfg" );
3✔
1043
  Core::read_extobj();
3✔
1044

1045
#ifdef _WIN32
1046
  Core::checkpoint( Core::Use_low_fragmentation_Heap() );
1047
#endif
1048

1049
#ifdef __linux__
1050
  Core::checkpoint( "checking libc version" );
3✔
1051
  Core::Check_libc_version();
3✔
1052
#endif
1053

1054
  Core::checkpoint( "init default itemdesc defaults" );
3✔
1055
  Core::gamestate.empty_itemdesc->doubleclick_range =
6✔
1056
      Core::settingsManager.ssopt.default_doubleclick_range;
3✔
1057
  Core::gamestate.empty_itemdesc->decay_time = Core::settingsManager.ssopt.default_decay_time;
3✔
1058

1059
  Core::checkpoint( "loading POL map file" );
3✔
1060
  if ( !Core::load_realms() )
3✔
1061
  {
1062
    POLLOG_ERRORLN(
×
1063
        "Unable to load Realms. Please make sure your Realms have been generated by "
1064
        "UOConvert and your RealmDataPath is set correctly in Pol.cfg." );
1065
    return 1;
×
1066
  }
1067

1068
  // PrintAllocationData();
1069

1070
  Core::checkpoint( "initializing IPC structures" );
3✔
1071
  Core::init_ipc_vars();
3✔
1072
  threadhelp::init_threadhelp();
3✔
1073

1074
#ifdef _WIN32
1075
  Core::InitializeSystemTrayHandling();
1076
#endif
1077

1078
  Core::checkpoint( "initializing sockets library" );
3✔
1079
  res = Network::init_sockets_library();
3✔
1080
  if ( res < 0 )
3✔
1081
  {
1082
    POLLOG_ERRORLN( "Unable to initialize sockets library." );
×
1083
    return 1;
×
1084
  }
1085

1086
  Core::checkpoint( "loading configuration" );
3✔
1087
  Core::load_data();
3✔
1088

1089
  Core::checkpoint( "loading system hooks" );
3✔
1090
  Core::load_system_hooks();
3✔
1091

1092
  Core::checkpoint( "loading packet hooks" );
3✔
1093
  Network::load_packet_hooks();
3✔
1094

1095
  Core::checkpoint( "loading auxservice configuration" );
3✔
1096
  Network::load_aux_services();
3✔
1097

1098
  Core::checkpoint( "reading menus" );
3✔
1099
  Core::Menu::read_menus();
3✔
1100

1101
  Core::checkpoint( "loading intrinsic weapons" );
3✔
1102
  Items::load_intrinsic_weapons();
3✔
1103
  Core::checkpoint( "validating intrinsic shield template" );
3✔
1104
  Items::validate_intrinsic_shield_template();
3✔
1105
  Core::checkpoint( "reading gameservers" );
3✔
1106
  Core::read_gameservers();
3✔
1107
  Core::checkpoint( "reading starting locations" );
3✔
1108
  Core::read_starting_locations();
3✔
1109

1110
  if ( testing )
3✔
1111
  {
1112
    Items::allocate_intrinsic_equipment_serials();
1✔
1113
    Core::stateManager.gflag_in_system_startup = false;
1✔
1114
    POLLOG_INFOLN( "Running POL test suite." );
1✔
1115
    bool res_test = Testing::run_pol_tests();
1✔
1116
    Core::cancel_all_trades();
1✔
1117
    Core::stop_gameclock();
1✔
1118
    Core::gamestate.deinitialize();
1✔
1119
    return !res_test;
1✔
1120
  }
1121

1122
  // PrintAllocationData();
1123
  POLLOG_INFOLN( "Reading data files:" );
2✔
1124
  {
1125
    Tools::Timer<> timer;
2✔
1126
    Core::checkpoint( "reading account data" );
2✔
1127
    Accounts::read_account_data();
2✔
1128

1129
    Core::checkpoint( "reading data" );
2✔
1130
    Core::read_data();
2✔
1131
    POLLOG_INFOLN( "Done! {} milliseconds.", timer.ellapsed() );
2✔
1132
  }
2✔
1133

1134

1135
  Items::allocate_intrinsic_equipment_serials();
2✔
1136
  Core::stateManager.gflag_in_system_startup = false;
2✔
1137

1138
  // PrintAllocationData();
1139

1140
  Core::checkpoint( "running start scripts" );
2✔
1141
  Core::run_start_scripts();
2✔
1142

1143
  Core::checkpoint( "starting client listeners" );
2✔
1144
  Core::start_uo_client_listeners();
2✔
1145

1146
  POLLOG_INFO( "Initialization complete.  POL is active.  Ctrl-C to stop.\n\n" );
2✔
1147

1148
  DEINIT_STARTLOG();
2✔
1149
  POLLOGLN( "{0:s} ({1:s}) compiled on {2:s} running.", "POL ", POL_VERSION_ID,
2✔
1150
            Clib::ProgramConfig::build_target(), Clib::ProgramConfig::build_datetime() );
4✔
1151

1152
  POLLOG_INFOLN( "Game is active." );
2✔
1153

1154
  Core::CoreSetSysTrayToolTip( "Running", Core::ToolTipPrioritySystem );
2✔
1155

1156
  Core::restart_pol_clocks();
2✔
1157
  Core::polclock_checkin();
2✔
1158

1159
  // this is done right after reading globals from pol.txt:
1160
  // checkpoint( "starting game clock" );
1161
  // start_gameclock();
1162

1163
  Core::checkpoint( "starting periodic tasks" );
2✔
1164
  Core::start_tasks();
2✔
1165

1166
  Core::checkpoint( "starting threads" );
2✔
1167
  Core::start_threads();
2✔
1168
  Network::start_aux_services();
2✔
1169
  Core::networkManager.initialize();
2✔
1170

1171
#ifdef _WIN32
1172
  Core::console_thread();
1173
  Core::checkpoint( "exit signal detected" );
1174
  Core::CoreSetSysTrayToolTip( "Shutting down", Core::ToolTipPriorityShutdown );
1175
#else
1176
  // On Linux, signals are directed to a particular thread, if we use pthread_sigmask like we're
1177
  // supposed to.
1178
  // therefore, we have to do this signal checking in this thread.
1179
  threadhelp::start_thread( Core::console_thread, "Console" );
2✔
1180

1181
  Core::catch_signals_thread();
2✔
1182
#endif
1183
  Core::checkpoint( "waiting for child threads to exit" );
2✔
1184
  // NOTE that it's possible that the thread_status thread not have exited yet..
1185
  // it signals the catch_signals_thread (this one) just before it exits.
1186
  // and on windows, we get here right after the console thread exits.
1187
  while ( threadhelp::child_threads )
2✔
1188
  {
1189
    Core::pol_sleep_ms( 1000 );
×
1190
  }
1191
  Core::checkpoint( "child threads have shut down" );
2✔
1192
  Core::cancel_all_trades();
2✔
1193
  Core::stop_gameclock();
2✔
1194
  POLLOG_INFOLN( "Shutting down..." );
2✔
1195

1196
  Core::checkpoint( "writing data" );
2✔
1197
  if ( Core::should_write_data() )
2✔
1198
  {
1199
    Core::CoreSetSysTrayToolTip( "Writing data files", Core::ToolTipPriorityShutdown );
2✔
1200
    POLLOG_INFO( "Writing data files..." );
2✔
1201

1202
    Core::PolLock lck;
2✔
1203
    s64 elapsed_ms;
1204
    Tools::Timer<> timer;
2✔
1205
    auto res_save = Core::write_data( {}, nullptr, nullptr, &elapsed_ms );
2✔
1206
    Core::SaveContext::ready();
2✔
1207
    if ( !res_save || !( *res_save ) )
2✔
NEW
1208
      POLLOG_INFOLN( "Data save failed!" );
×
1209
    else
1210
      POLLOG_INFOLN( "Data save completed in {} ms. {} total.", elapsed_ms, timer.ellapsed() );
2✔
1211
  }
2✔
1212
  else
1213
  {
1214
    if ( Clib::passert_shutdown_due_to_assertion && Clib::passert_nosave )
×
1215
      POLLOG_INFOLN( "Not writing data due to assertion failure." );
×
1216
    else if ( Plib::systemstate.config.inhibit_saves )
×
1217
      POLLOG_INFOLN( "Not writing data due to pol.cfg InhibitSaves=1 setting." );
×
1218
  }
1219
  Core::gamestate.deinitialize();
2✔
1220
  return Clib::exit_code;
2✔
1221
}
3✔
1222

1223
int xmain_outer( bool testing )
3✔
1224
{
1225
  try
1226
  {
1227
    return xmain_inner( testing );
3✔
1228
  }
1229
  catch ( std::exception& )
×
1230
  {
1231
    if ( Core::stateManager.last_checkpoint != nullptr )
×
1232
    {
1233
      POLLOG_INFOLN( "Server Shutdown: {}", Core::stateManager.last_checkpoint );
×
1234
      // pol_sleep_ms( 10000 );
1235
    }
1236
    Core::gamestate.deinitialize();
×
1237

1238
    throw;
×
1239
  }
×
1240
}
1241
}  // 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