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

polserver / polserver / 13163655424

05 Feb 2025 06:02PM UTC coverage: 58.476% (+0.4%) from 58.057%
13163655424

push

github

web-flow
Expansion helper classes for central handling of features (#756)

* use uoexpansion enums for A9, B9 flags
added TOL to uoexpansion
currently unsure where this will end

simplified account initialisation, added errorcheck

* Expansion classes for server and account
fixed flags

* use of AccountExpansion,ServerExpansion
extended tests
splitted unittest file
added missing test for Min/MaxAttackRange

* more micro performance for world saving
use all 4 cores in CI builds
fix compiler flag warning on windows

* save test for weight_multiplier

* enable gothic,rustictiles when using HSA expansion

* removed ancient unused files

* addressed comments
renamed ServerExpansion to ServerFeatures
added method for expansion name

* got rid of client UOExpansionFlag which is the same as the accout
expansion

* updated core-changes and docs

* updated generated ssopt

* test for tooltips
changed method names
removed/clarified comments

* npc hitchance was never saved
added missing npc save tests

* hitchance bug exists also for items...
added missing test for movemode

465 of 525 new or added lines in 30 files covered. (88.57%)

16 existing lines in 7 files now uncovered.

41768 of 71427 relevant lines covered (58.48%)

388521.09 hits per line

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

18.98
/pol-core/pol/miscmsg.cpp
1
/** @file
2
 *
3
 * @par History
4
 * - 2005/11/25 MuadDib:   Added PKTBI_BF::TYPE_SESPAM: to do away with spam.
5
 * - 2005/11/23 MuadDib:   Altered handle_mode_set for 0x72 Packet. Nows reads
6
 *                         from combat.cfg for warmode_wait object. Sends the
7
 *                         0x77 Packet and returns 0 when out of timer.
8
 * - 2005/12/09 MuadDib:   Added TYPE_CLIENT_LANGUAGE for setting member uclang.
9
 * - 2006/05/24 Shinigami: Added PKTBI_BF::TYPE_CHARACTER_RACE_CHANGER to support Elfs
10
 * - 2006/05/30 Shinigami: Changed params of character_race_changer_handler()
11
 * - 2009/07/23 MuadDib:   updates for new Enum::Packet IDs
12
 * - 2009/08/14 Turley:    Added PKTBI_BF::TYPE_SCREEN_SIZE & TYPE_CLOSED_STATUS_GUMP (anti spam)
13
 * - 2009/09/03 MuadDib:   Relocation of account related cpp/h
14
 * - 2009/09/03 MuadDib:   Relocation of multi related cpp/h
15
 * - 2009/09/06 Turley:    Changed Version checks to bitfield client->ClientType
16
 *                         Added 0xE1 packet (UO3D clienttype packet)
17
 * - 2009/11/19 Turley:    ssopt.core_sends_season & .core_handled_tags - Tomi
18
 * - 2009/12/03 Turley:    toggle gargoyle flying support
19
 */
20

21

22
/* MISCMSG.CPP: Miscellaneous message handlers.  Handlers shouldn't stay here long,
23
   only until they find a better home - but this is better than putting them in POL.CPP. */
24

25
#include <algorithm>
26
#include <cstddef>
27
#include <ctype.h>
28
#include <iterator>
29
#include <string>
30

31
#include "../bscript/eprog.h"
32
#include "../bscript/impstr.h"
33
#include "../clib/clib_endian.h"
34
#include "../clib/fdump.h"
35
#include "../clib/logfacility.h"
36
#include "../clib/rawtypes.h"
37
#include "../clib/refptr.h"
38
#include "../clib/stlutil.h"
39
#include "../plib/systemstate.h"
40
#include "../plib/uconst.h"
41
#include "accounts/account.h"
42
#include "cmbtcfg.h"
43
#include "fnsearch.h"
44
#include "gameclck.h"
45
#include "globals/settings.h"
46
#include "globals/uvars.h"
47
#include "guilds.h"
48
#include "mobile/attribute.h"
49
#include "mobile/charactr.h"
50
#include "module/uomod.h"
51
#include "multi/customhouses.h"
52
#include "multi/multi.h"
53
#include "network/cgdata.h"
54
#include "network/client.h"
55
#include "network/clientio.h"
56
#include "network/packethelper.h"
57
#include "network/packets.h"
58
#include "network/pktboth.h"
59
#include "network/pktdef.h"
60
#include "network/pktin.h"
61
#include "network/sockio.h"
62
#include "party.h"
63
#include "polclass.h"
64
#include "realms/realm.h"
65
#include "scrstore.h"
66
#include "spells.h"
67
#include "systems/suspiciousacts.h"
68
#include "tooltips.h"
69
#include "ufunc.h"
70
#include "uobject.h"
71
#include "uoexec.h"
72
#include "uoscrobj.h"
73

74

75
namespace Pol
76
{
77
namespace Module
78
{
79
void character_race_changer_handler( Network::Client* client, Core::PKTBI_BF* msg );
80
}
81
namespace Core
82
{
83
using namespace Network;
84

85
void handle_unknown_packet( Network::ThreadedClient* session );
86
void handle_unknown_packet( Network::Client* client )
×
87
{
88
  handle_unknown_packet( client->session() );
×
89
}
×
90

91
void party_cmd_handler( Client* client, PKTBI_BF* msg );
92

93
void OnGuildButton( Client* client );
94
void OnQuestButton( Client* client );
95
void OnChatButton( Client* client );
96

97
void handle_bulletin_boards( Client* client, PKTBI_71* /*msg*/ )
×
98
{
99
  handle_unknown_packet( client );
×
100
}
×
101

102
void handle_mode_set( Client* client, PKTBI_72* msg )
×
103
{
104
  if ( client->chr->warmode_wait > read_gameclock() )
×
105
  {
106
    send_move( client, client->chr );
×
107
    return;
×
108
  }
109
  else
110
  {
111
    client->chr->warmode_wait = read_gameclock() + settingsManager.combat_config.warmode_delay;
×
112
  }
113

114
  bool msg_warmode = msg->warmode ? true : false;
×
115

116
  // FIXME: Should reply with 0x77 packet!? (so says various docs!) [TJ]
117
  transmit( client, msg, sizeof *msg );
×
118

119
  client->chr->set_warmode( msg_warmode );
×
120
}
121

122
void handle_keep_alive( Network::Client* client, PKTBI_73* msg )
2✔
123
{
124
  transmit( client, msg, sizeof *msg );
2✔
125
}
2✔
126

127
void handle_rename_char( Client* client, PKTIN_75* msg )
×
128
{
129
  Mobile::Character* chr = find_character( cfBEu32( msg->serial ) );
×
130
  if ( chr != nullptr )
×
131
  {
132
    if ( client->chr->can_rename( chr ) )
×
133
    {
134
      msg->name[sizeof msg->name - 1] = '\0';
×
135
      // check for legal characters
136
      for ( char* p = msg->name; *p; p++ )
×
137
      {
138
        // only allow: a-z, A-Z & spaces
139
        if ( *p != ' ' && !isalpha( *p ) )
×
140
        {
141
          std::string tmp = fmt::format(
142
              "Client#{} (account {}) attempted an invalid rename (packet {:#x}):\n{}",
143
              client->instance_, ( client->acct != nullptr ) ? client->acct->name() : "unknown",
×
144
              (int)msg->msgtype, msg->name );
×
145
          Clib::fdump( std::back_inserter( tmp ), msg->name,
×
146
                       static_cast<int>( strlen( msg->name ) ) );
×
147
          POLLOG_INFOLN( tmp );
×
148
          *p = '\0';
×
149
          send_sysmessage( client, "Invalid name!" );
×
150
          return;  // dave 12/26 if invalid name, do not apply to chr!
×
151
        }
×
152
      }
153
      chr->setname( msg->name );
×
154
    }
155
    else
156
    {
157
      send_sysmessage( client, "I can't rename that." );
×
158
    }
159
  }
160
  else
161
  {
162
    send_sysmessage( client, "I can't find that." );
×
163
  }
164
}
165

166
void handle_msg_B5( Client* client, PKTIN_B5* /*msg*/ )
×
167
{
168
  OnChatButton( client );
×
169
}
×
170

171
void handle_char_profile_request( Client* client, PKTBI_B8_IN* msg )
×
172
{
173
  ref_ptr<Bscript::EScriptProgram> prog =
174
      find_script( "misc/charprofile", true, Plib::systemstate.config.cache_interactive_scripts );
×
175
  if ( prog.get() != nullptr )
×
176
  {
177
    Mobile::Character* mobile;
178

179
    if ( msg->mode == msg->MODE_REQUEST )
×
180
    {
181
      mobile = system_find_mobile( cfBEu32( msg->profile_request.serial ) );
×
182
      if ( mobile == nullptr )
×
183
        return;
×
184
      client->chr->start_script( prog.get(), false, new Module::ECharacterRefObjImp( mobile ),
×
185
                                 new Bscript::BLong( msg->mode ), new Bscript::BLong( 0 ) );
×
186
    }
187
    else if ( msg->mode == msg->MODE_UPDATE )
×
188
    {
189
      mobile = system_find_mobile( cfBEu32( msg->profile_update.serial ) );
×
190
      if ( mobile == nullptr )
×
191
        return;
×
192
      u16* themsg = msg->profile_update.wtext;
×
193
      int intextlen = ( cfBEu16( msg->msglen ) - 12 ) / sizeof( msg->profile_update.wtext[0] );
×
194

195
      if ( intextlen < 0 )
×
196
        intextlen = 0;
×
197
      if ( intextlen > SPEECH_MAX_LEN )
×
198
        intextlen = SPEECH_MAX_LEN;
×
199
      std::string text = Bscript::String::fromUTF16( themsg, intextlen, true );
×
200
      client->chr->start_script( prog.get(), false, new Module::ECharacterRefObjImp( mobile ),
×
201
                                 new Bscript::BLong( msg->mode ), new Bscript::String( text ) );
×
202
    }
×
203
  }
204
}
×
205

206
void handle_msg_BB( Client* client, PKTBI_BB* /*msg*/ )
×
207
{
208
  handle_unknown_packet( client );
×
209
}
×
210

211
void handle_client_version( Client* client, PKTBI_BD* msg )
2✔
212
{
213
  u16 len = cfBEu16( msg->msglen ) - 3;
2✔
214
  if ( len < 100 )
2✔
215
  {
216
    int c = 0;
2✔
217
    char ch;
218
    std::string ver2 = "";
2✔
219
    while ( c < len )
16✔
220
    {
221
      ch = msg->version[c];
16✔
222
      if ( ch == 0 )
16✔
223
        break;  // seems to be null-terminated
2✔
224
      ver2 += ch;
14✔
225
      ++c;
14✔
226
    }
227
    client->setversion( ver2 );
2✔
228

229
    VersionDetailStruct vers_det;
230
    client->itemizeclientversion( ver2, vers_det );
2✔
231
    client->setversiondetail( vers_det );
2✔
232

233
    if ( client->compareVersion( CLIENT_VER_70331 ) )
2✔
234
      client->setClientType( CLIENTTYPE_70331 );
×
235
    else if ( client->compareVersion( CLIENT_VER_70300 ) )
2✔
236
      client->setClientType( CLIENTTYPE_70300 );
×
237
    else if ( client->compareVersion( CLIENT_VER_70130 ) )
2✔
238
      client->setClientType( CLIENTTYPE_70130 );
×
239
    else if ( client->compareVersion( CLIENT_VER_7090 ) )
2✔
240
      client->setClientType( CLIENTTYPE_7090 );
2✔
241
    else if ( client->compareVersion( CLIENT_VER_7000 ) )
×
242
      client->setClientType( CLIENTTYPE_7000 );
×
243
    else if ( client->compareVersion( CLIENT_VER_60142 ) )
×
244
      client->setClientType( CLIENTTYPE_60142 );
×
245
    else if ( client->compareVersion( CLIENT_VER_6017 ) )  // Grid-loc support
×
246
      client->setClientType( CLIENTTYPE_6017 );
×
247
    else if ( client->compareVersion( CLIENT_VER_5020 ) )
×
248
      client->setClientType( CLIENTTYPE_5020 );
×
249
    else if ( client->compareVersion( CLIENT_VER_5000 ) )
×
250
      client->setClientType( CLIENTTYPE_5000 );
×
251
    else if ( client->compareVersion( CLIENT_VER_4070 ) )
×
252
      client->setClientType( CLIENTTYPE_4070 );
×
253
    else if ( client->compareVersion( CLIENT_VER_4000 ) )
×
254
      client->setClientType( CLIENTTYPE_4000 );
×
255

256
    if ( settingsManager.ssopt.core_sends_season )
2✔
257
      send_season_info(
2✔
258
          client );  // Scott 10/11/2007 added for login fixes and handling 1.x clients.
259
                     // Season info needs to check client version to keep from crashing 1.x
260
                     // version not set until shortly after login complete.
261
    // send_feature_enable(client); //dave commented out 8/21/03, unexpected problems with people
262
    // sending B9 via script with this too.
263
    if ( client->acctSupports( Plib::ExpansionVersion::AOS ) )
2✔
264
    {
265
      send_object_cache( client, client->chr );
2✔
266
    }
267
  }
2✔
268
  else
269
  {
270
    POLLOG_INFOLN( "Suspect string length in PKTBI_BD packet: {}", len );
×
271
  }
272
}
2✔
273

274
void ext_stats_in( Client* client, PKTBI_BF* msg )
×
275
{
276
  if ( settingsManager.ssopt.core_handled_locks )
×
277
  {
278
    const Mobile::Attribute* attrib = nullptr;
×
279
    switch ( msg->extstatin.stat )
×
280
    {
281
    case PKTBI_BF_1A::STAT_STR:
×
282
      attrib = gamestate.pAttrStrength;
×
283
      break;
×
284
    case PKTBI_BF_1A::STAT_DEX:
×
285
      attrib = gamestate.pAttrDexterity;
×
286
      break;
×
287
    case PKTBI_BF_1A::STAT_INT:
×
288
      attrib = gamestate.pAttrIntelligence;
×
289
      break;
×
290
    default:  // sent an illegal stat. Should report to console?
×
291
      return;
×
292
    }
293

294
    if ( attrib == nullptr )  // there's no attribute for this (?)
×
295
      return;
×
296

297
    u8 state = msg->extstatin.mode;
×
298
    if ( state > 2 )  // FIXME hard-coded value
×
299
      return;
×
300

301
    client->chr->attribute( attrib->attrid ).lock( state );
×
302
  }
303
}
304

305
void handle_msg_BF( Client* client, PKTBI_BF* msg )
8✔
306
{
307
  UObject* obj = nullptr;
8✔
308
  Multi::UMulti* multi = nullptr;
8✔
309
  Multi::UHouse* house = nullptr;
8✔
310
  switch ( cfBEu16( msg->subcmd ) )
8✔
311
  {
312
  case PKTBI_BF::TYPE_CLIENT_LANGUAGE:
2✔
313
    client->chr->uclang = Clib::strlowerASCII( msg->client_lang );
2✔
314
    break;
2✔
315
  case PKTBI_BF::TYPE_REQ_FULL_CUSTOM_HOUSE:
×
NEW
316
    if ( !client->acctSupports( Plib::ExpansionVersion::AOS ) )
×
317
      return;
×
318
    multi = system_find_multi( cfBEu32( msg->reqfullcustomhouse.house_serial ) );
×
319
    if ( multi != nullptr )
×
320
    {
321
      house = multi->as_house();
×
322
      if ( house != nullptr )
×
323
      {
NEW
324
        if ( client->acctSupports( Plib::ExpansionVersion::AOS ) )
×
325
        {
326
          send_object_cache( client, (UObject*)( house ) );
×
327
        }
328
        // consider sending working design to certain players, to assist building, or GM help
329
        CustomHousesSendFull( house, client, Multi::HOUSE_DESIGN_CURRENT );
×
330
      }
331
    }
332
    break;
×
333
  case PKTBI_BF::TYPE_OBJECT_CACHE:
1✔
334
    if ( !client->acctSupports( Plib::ExpansionVersion::AOS ) )
1✔
335
      return;
×
336
    obj = system_find_object( cfBEu32( msg->objectcache.serial ) );
1✔
337
    if ( obj != nullptr )
1✔
338
    {
339
      SendAOSTooltip( client, obj );
1✔
340
    }
341
    break;
1✔
342
  case PKTBI_BF::TYPE_SESPAM:
×
343
    return;
×
344
    break;
345
  case PKTBI_BF::TYPE_SPELL_SELECT:
×
346
    do_cast( client, cfBEu16( msg->spellselect.selected_spell ) );
×
347
    break;
×
348
  case PKTBI_BF::TYPE_CHARACTER_RACE_CHANGER:
×
349
    Module::character_race_changer_handler( client, msg );
×
350
    break;
×
351
  case PKTBI_BF::TYPE_PARTY_SYSTEM:
×
352
    party_cmd_handler( client, msg );
×
353
    break;
×
354
  case PKTBI_BF::TYPE_EXTENDED_STATS_IN:
×
355
    ext_stats_in( client, msg );
×
356
    break;
×
357
  case PKTBI_BF::TYPE_CLOSED_STATUS_GUMP:
×
358
    return;
×
359
    break;
360
  case PKTBI_BF::TYPE_SCREEN_SIZE:
×
361
    return;
×
362
    break;
363
  case PKTBI_BF::TYPE_TOGGLE_FLYING:
×
364
    if ( client->chr->race == Plib::RACE_GARGOYLE )
×
365
    {
366
      // FIXME: add checks if its possible to stand with new movemode
367
      client->chr->movemode = ( Plib::MOVEMODE )( client->chr->movemode ^ Plib::MOVEMODE_FLY );
×
368
      send_move_mobile_to_nearby_cansee( client->chr );
×
369
      send_goxyz( client, client->chr );
×
370
    }
371
    break;
×
372
  case PKTBI_BF::TYPE_CLIENTTYPE:
2✔
373
    client->UOExpansionFlagClient = ctBEu32( msg->clienttype.clientflag );
2✔
374
    break;
2✔
375
  case PKTBI_BF::TYPE_POPUP_MENU_REQUEST:
×
376
  {
377
    ref_ptr<Bscript::EScriptProgram> prog =
378
        find_script( "misc/popupmenu", true, Plib::systemstate.config.cache_interactive_scripts );
×
379
    if ( prog.get() == nullptr )
×
380
      break;
×
381
    u32 serial = cfBEu32( msg->serial_request_popup_menu );
×
382
    if ( IsCharacter( serial ) )
×
383
    {
384
      Pol::Mobile::Character* chr = system_find_mobile( serial );
×
385
      if ( chr == nullptr )
×
386
        break;
×
387
      client->chr->start_script( prog.get(), false, new Pol::Module::ECharacterRefObjImp( chr ) );
×
388
    }
389
    else
390
    {
391
      Pol::Items::Item* item = system_find_item( serial );
×
392
      if ( item == nullptr )
×
393
        break;
×
394
      client->chr->start_script( prog.get(), false, item->make_ref() );
×
395
    }
396
    break;
×
397
  }
×
398
  case PKTBI_BF::TYPE_POPUP_ENTRY_SELECT:
×
399
  {
400
    if ( client->gd->on_popup_menu_selection == nullptr )
×
401
    {
402
      POLLOG_INFOLN( "{}/{} tried to use a popup menu, but none was active.", client->acct->name(),
×
403
                     client->chr->name() );
×
404
      break;
×
405
    }
406

407
    u32 serial = cfBEu32( msg->popupselect.serial );
×
408
    u16 id = cfBEu16( msg->popupselect.entry_tag );
×
409
    client->gd->on_popup_menu_selection( client, serial, id );
×
410
    break;
×
411
  }
412
  case PKTBI_BF::TYPE_BOAT_MOVE:
3✔
413
  {
414
    Mobile::Character* chr = client->chr;
3✔
415
    multi = chr->realm()->find_supporting_multi( client->chr->pos3d() );
3✔
416

417
    if ( multi == nullptr )
3✔
418
    {
419
      SuspiciousActs::BoatMoveNoMulti( client );
×
420
      break;
×
421
    }
422

423
    if ( !multi->script_isa( Core::POLCLASS_BOAT ) )
3✔
424
    {
425
      SuspiciousActs::BoatMoveNotBoatMulti( client );
×
426
      break;
×
427
    }
428

429
    Multi::UBoat* boat = static_cast<Multi::UBoat*>( multi );
3✔
430
    if ( boat->pilot() != chr )
3✔
431
    {
432
      SuspiciousActs::BoatMoveNotPilot( client, multi->serial );
×
433
      break;
×
434
    }
435

436
    if ( msg->boatmove.direction > 7 || msg->boatmove.speed > 2 )
3✔
437
    {
438
      SuspiciousActs::BoatMoveOutOfRangeParameters( client, multi->serial, msg->boatmove.direction,
×
439
                                                    msg->boatmove.speed );
×
440
      break;
×
441
    }
442

443
    Module::UOExecutorModule* process = multi->process();
3✔
444
    if ( !process )
3✔
445
    {
446
      break;
×
447
    }
448

449
    auto relative_direction =
450
        static_cast<Core::UFACING>( ( msg->boatmove.direction - boat->boat_facing() + 8 ) & 7 );
3✔
451

452
    process->uoexec().signal_event( new Module::BoatMovementEvent(
6✔
453
        chr, msg->boatmove.speed, msg->boatmove.direction, relative_direction ) );
3✔
454

455
    break;
3✔
456
  }
457
  default:
×
458
    handle_unknown_packet( client );
×
459
  }
460
}
461

462
void handle_unknown_C4( Client* client, PKTOUT_C4* /*msg*/ )
×
463
{
464
  handle_unknown_packet( client );
×
465
}
×
466

467
void handle_update_range_change( Client* client, PKTBI_C8* msg )
2✔
468
{
469
  client->set_update_range_by_client( msg->range );
2✔
470
}
2✔
471

472
void handle_krrios_packet( Client* client, PKTBI_F0* msg )
×
473
{
474
  auto* me = client->chr;
×
475
  switch ( msg->subcmd )
×
476
  {
477
  case PKTBI_F0::QUERY_PARTY:
×
478
  {
479
    if ( !settingsManager.ssopt.enable_worldmap_packets )
×
480
      break;
×
481

482
    if ( me->has_party() )
×
483
    {
484
      Party* party = me->party();
×
485
      Network::PktHelper::PacketOut<Network::PktOut_F0_Sub01> outMsg;
×
486
      outMsg->offset += 2;                              // len
×
487
      outMsg->Write<u8>( PKTBI_F0::QUERY_PARTY + 1U );  // sub, response is +1
×
488
      for ( auto member : party->get_members() )
×
489
      {
490
        if ( member->serial == me->serial )
×
491
          continue;
×
492

493
        if ( me->is_visible_to_me( member ) )
×
494
          continue;
×
495

496
        outMsg->Write<u32>( member->serial_ext );
×
497
        outMsg->WriteFlipped<u16>( member->x() );
×
498
        outMsg->WriteFlipped<u16>( member->y() );
×
499
        outMsg->Write<u8>( member->realm()->getUOMapID() );
×
500
      }
×
501

502
      if ( outMsg->offset != 4 )  // only send if there is an update
×
503
      {
504
        outMsg->Write<u32>( 0U );  // end of list marker
×
505
        u16 len = outMsg->offset;
×
506
        outMsg->offset = 1;
×
507
        outMsg->WriteFlipped<u16>( len );
×
508
        outMsg.Send( client, len );
×
509
      }
510
    }
×
511
    break;
×
512
  }
513
  case PKTBI_F0::QUERY_GUILD:
×
514
  {
515
    if ( !settingsManager.ssopt.enable_worldmap_packets )
×
516
      break;
×
517

518
    Guild* guild = me->guild();
×
519
    u8 locations = msg->query_guild.include_locations > 0 ? 1U : 0U;
×
520
    if ( guild != nullptr )
×
521
    {
522
      Network::PktHelper::PacketOut<Network::PktOut_F0_Sub02> outMsg;
×
523
      outMsg->offset += 2;                              // len
×
524
      outMsg->Write<u8>( PKTBI_F0::QUERY_GUILD + 1U );  // sub, response is +1
×
525
      outMsg->Write<u8>( locations );
×
526
      for ( auto member : guild->get_members() )
×
527
      {
528
        if ( member->serial == me->serial )
×
529
          continue;
×
530

531
        if ( locations && me->is_visible_to_me( member ) )
×
532
          continue;
×
533

534
        outMsg->Write<u32>( member->serial_ext );
×
535
        if ( locations )
×
536
        {
537
          outMsg->WriteFlipped<u16>( member->x() );
×
538
          outMsg->WriteFlipped<u16>( member->y() );
×
539
          outMsg->Write<u8>( member->realm()->getUOMapID() );
×
540

541
          if ( member->dead() )
×
542
            outMsg->Write<u8>( 0U );
×
543
          else
544
          {
545
            int current = member->vital( networkManager.uoclient_general.hits.id ).current_ones();
×
546
            int max = member->vital( networkManager.uoclient_general.hits.id ).maximum_ones();
×
547
            u8 ratio = static_cast<u8>( 100 * current / ( max < 1 ? 1 : max ) );
×
548
            outMsg->Write<u8>( ratio );  // hits
×
549
          }
550
        }
551
      }
×
552
      if ( outMsg->offset != 5 )  // only send if there is an update
×
553
      {
554
        outMsg->Write<u32>( 0U );  // end of list marker
×
555
        u16 len = outMsg->offset;
×
556
        outMsg->offset = 1;
×
557
        outMsg->WriteFlipped<u16>( len );
×
558
        outMsg.Send( client, len );
×
559
      }
560
    }
×
561
    break;
×
562
  }
563
  default:
×
564
    handle_unknown_packet( client );
×
565
    break;
×
566
  }
567
}
×
568

569
void handle_open_uo_store( Client* client, PKTIN_FA* /*msg*/ )
×
570
{
571
  handle_unknown_packet( client );
×
572
}
×
573

574
void handle_update_view_public_house_content( Client* client, PKTIN_FB* /*msg*/ )
×
575
{
576
  handle_unknown_packet( client );
×
577
}
×
578

579
void handle_allnames( Client* client, PKTBI_98_IN* msg )
×
580
{
581
  u32 serial = cfBEu32( msg->serial );
×
582
  Mobile::Character* the_mob = find_character( serial );
×
583
  if ( the_mob == nullptr )
×
584
    return;
×
585
  if ( !client->chr->is_visible_to_me( the_mob ) )
×
586
    return;
×
587

588
  PktHelper::PacketOut<PktOut_98> msgOut;
×
589
  msgOut->WriteFlipped<u16>( 37u );  // static length
×
590
  msgOut->Write<u32>( the_mob->serial_ext );
×
591
  msgOut->Write( Clib::strUtf8ToCp1252( the_mob->name() ).c_str(), 30, false );
×
592
  msgOut.Send( client );
×
593
}
×
594

595
void handle_se_object_list( Client* client, PKTBI_D6_IN* msgin )
4✔
596
{
597
  UObject* obj = nullptr;
4✔
598
  int length = cfBEu16( msgin->msglen ) - 3;
4✔
599
  if ( length < 0 || ( length % 4 ) != 0 )
4✔
600
    return;
×
601
  int count = length / 4;
4✔
602

603
  for ( int i = 0; i < count; ++i )
8✔
604
  {
605
    obj = system_find_object( cfBEu32( msgin->serials[i].serial ) );
4✔
606
    if ( obj != nullptr )
4✔
607
      SendAOSTooltip( client, obj );
4✔
608
  }
609
}
610

611
void handle_ef_seed( Client* client, PKTIN_EF* msg )
2✔
612
{
613
  VersionDetailStruct detail;
614
  detail.major = cfBEu32( msg->ver_Major );
2✔
615
  detail.minor = cfBEu32( msg->ver_Minor );
2✔
616
  detail.rev = cfBEu32( msg->ver_Revision );
2✔
617
  detail.patch = cfBEu32( msg->ver_Patch );
2✔
618
  client->setversiondetail( detail );
2✔
619
  if ( client->compareVersion( CLIENT_VER_70331 ) )
2✔
620
    client->setClientType( CLIENTTYPE_70331 );
×
621
  else if ( client->compareVersion( CLIENT_VER_70300 ) )
2✔
622
    client->setClientType( CLIENTTYPE_70300 );
×
623
  else if ( client->compareVersion( CLIENT_VER_70130 ) )
2✔
624
    client->setClientType( CLIENTTYPE_70130 );
×
625
  else if ( client->compareVersion( CLIENT_VER_7090 ) )
2✔
626
    client->setClientType( CLIENTTYPE_7090 );
2✔
627
  else if ( client->compareVersion( CLIENT_VER_7000 ) )
×
628
    client->setClientType( CLIENTTYPE_7000 );
×
629
  else if ( client->compareVersion( CLIENT_VER_60142 ) )
×
630
    client->setClientType( CLIENTTYPE_60142 );
×
631
  else if ( client->compareVersion( CLIENT_VER_6017 ) )  // Grid-loc support
×
632
    client->setClientType( CLIENTTYPE_6017 );
×
633
  else if ( client->compareVersion( CLIENT_VER_5020 ) )
×
634
    client->setClientType( CLIENTTYPE_5020 );
×
635
  else if ( client->compareVersion( CLIENT_VER_5000 ) )
×
636
    client->setClientType( CLIENTTYPE_5000 );
×
637
  else if ( client->compareVersion( CLIENT_VER_4070 ) )
×
638
    client->setClientType( CLIENTTYPE_4070 );
×
639
  else if ( client->compareVersion( CLIENT_VER_4000 ) )
×
640
    client->setClientType( CLIENTTYPE_4000 );
×
641

642
  // detail->patch is since 5.0.7 always numeric, so no need to make it complicated
643
  OSTRINGSTREAM os;
2✔
644
  os << detail.major << "." << detail.minor << "." << detail.rev << "." << detail.patch;
2✔
645
  client->setversion( OSTRINGSTREAM_STR( os ) );
2✔
646
}
2✔
647

648
void handle_e1_clienttype( Client* client, PKTIN_E1* msg )
×
649
{
650
  switch ( cfBEu32( msg->clienttype ) )
×
651
  {
652
  case PKTIN_E1::CLIENTTYPE_KR:
×
653
    client->setClientType( CLIENTTYPE_UOKR );
×
654
    break;
×
655
  case PKTIN_E1::CLIENTTYPE_SA:
×
656
    client->setClientType( CLIENTTYPE_UOSA );
×
657
    break;
×
658
  default:
×
659
    INFO_PRINTLN( "Unknown client type send with packet 0xE1 : {:#x}",
×
660
                  static_cast<unsigned long>( cfBEu32( msg->clienttype ) ) );
×
661
    break;
×
662
  }
663
}
×
664

665

666
/**
667
 * Handler for a 0xD7 packet
668
 */
669
void handle_aos_commands( Client* client, PKTBI_D7* msg )
×
670
{
671
  // nullptr prevention, no need to disturb if client or character is not found
672
  if ( client == nullptr || client->chr == nullptr )
×
673
    return;
×
674

675
  /// Checks that serial written inside packet matches sending character's serial
676
  u32 serial = cfBEu32( msg->serial );
×
677
  if ( client && client->chr && client->chr->serial != serial )
×
678
  {
679
    INFO_PRINTLN( "Ignoring spoofed packet 0xD7 from character {:#x} trying to spoof {:#x}",
×
680
                  client->chr->serial, serial );
×
681
    return;
×
682
  }
683

684
  switch ( cfBEu16( msg->subcmd ) )
×
685
  {
686
  case PKTBI_D7::CUSTOM_HOUSE_BACKUP:
×
687
    Multi::CustomHousesBackup( msg );
×
688
    break;
×
689

690
  case PKTBI_D7::CUSTOM_HOUSE_RESTORE:
×
691
    Multi::CustomHousesRestore( msg );
×
692
    break;
×
693

694
  case PKTBI_D7::CUSTOM_HOUSE_COMMIT:
×
695
    Multi::CustomHousesCommit( msg );
×
696
    break;
×
697

698
  case PKTBI_D7::CUSTOM_HOUSE_ERASE:
×
699
    Multi::CustomHousesErase( msg );
×
700
    break;
×
701

702
  case PKTBI_D7::CUSTOM_HOUSE_ADD:
×
703
    Multi::CustomHousesAdd( msg );
×
704
    break;
×
705

706
  case PKTBI_D7::CUSTOM_HOUSE_QUIT:
×
707
    Multi::CustomHousesQuit( msg );
×
708
    break;
×
709

710
  case PKTBI_D7::CUSTOM_HOUSE_ADD_MULTI:
×
711
    Multi::CustomHousesAddMulti( msg );
×
712
    break;
×
713

714
  case PKTBI_D7::CUSTOM_HOUSE_SYNCH:
×
715
    Multi::CustomHousesSynch( msg );
×
716
    break;
×
717

718
  case PKTBI_D7::CUSTOM_HOUSE_CLEAR:
×
719
    Multi::CustomHousesClear( msg );
×
720
    break;
×
721

722
  case PKTBI_D7::CUSTOM_HOUSE_SELECT_FLOOR:
×
723
    Multi::CustomHousesSelectFloor( msg );
×
724
    break;
×
725

726
  case PKTBI_D7::CUSTOM_HOUSE_REVERT:
×
727
    Multi::CustomHousesRevert( msg );
×
728
    break;
×
729
  case PKTBI_D7::CUSTOM_HOUSE_SELECT_ROOF:
×
730
    Multi::CustomHousesRoofSelect( msg );
×
731
    break;
×
732
  case PKTBI_D7::CUSTOM_HOUSE_DELETE_ROOF:
×
733
    Multi::CustomHousesRoofRemove( msg );
×
734
    break;
×
735
  case PKTBI_D7::GUILD_BUTTON:
×
736
    OnGuildButton( client );
×
737
    break;
×
738
  case PKTBI_D7::QUEST_BUTTON:
×
739
    OnQuestButton( client );
×
740
    break;
×
741
  // missing combat book abilities
742
  default:
×
743
    handle_unknown_packet( client );
×
744
  }
745
}
746

747
void OnGuildButton( Client* client )
×
748
{
749
  ref_ptr<Bscript::EScriptProgram> prog =
750
      find_script( "misc/guildbutton", true, Plib::systemstate.config.cache_interactive_scripts );
×
751
  if ( prog.get() != nullptr )
×
752
  {
753
    client->chr->start_script( prog.get(), false );
×
754
  }
755
}
×
756

757
void OnQuestButton( Client* client )
×
758
{
759
  ref_ptr<Bscript::EScriptProgram> prog =
760
      find_script( "misc/questbutton", true, Plib::systemstate.config.cache_interactive_scripts );
×
761
  if ( prog.get() != nullptr )
×
762
  {
763
    client->chr->start_script( prog.get(), false );
×
764
  }
765
}
×
766

767
void OnChatButton( Client* client )
×
768
{
769
  ref_ptr<Bscript::EScriptProgram> prog =
770
      find_script( "misc/chatbutton", true, Plib::systemstate.config.cache_interactive_scripts );
×
771
  if ( prog.get() != nullptr )
×
772
  {
773
    client->chr->start_script( prog.get(), false );
×
774
  }
775
}
×
776
}  // namespace Core
777
}  // 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