Just a few renames.
[getmangos.git] / src / game / WorldSession.cpp
blob94d7cca078ec4f282149b00304ec0f8194d15ae3
1 /*
2 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 /** \file
20 \ingroup u2w
23 #include "WorldSocket.h" // must be first to make ACE happy with ACE includes in it
24 #include "Common.h"
25 #include "Database/DatabaseEnv.h"
26 #include "Log.h"
27 #include "Opcodes.h"
28 #include "WorldPacket.h"
29 #include "WorldSession.h"
30 #include "Player.h"
31 #include "ObjectMgr.h"
32 #include "Group.h"
33 #include "Guild.h"
34 #include "World.h"
35 #include "ObjectAccessor.h"
36 #include "BattleGroundMgr.h"
37 #include "SocialMgr.h"
38 #include "zlib/zlib.h"
40 /// WorldSession constructor
41 WorldSession::WorldSession(uint32 id, WorldSocket *sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale) :
42 LookingForGroup_auto_join(false), LookingForGroup_auto_add(false), m_muteTime(mute_time),
43 _player(NULL), m_Socket(sock),_security(sec), _accountId(id), m_expansion(expansion),
44 m_sessionDbcLocale(sWorld.GetAvailableDbcLocale(locale)), m_sessionDbLocaleIndex(objmgr.GetIndexForLocale(locale)),
45 _logoutTime(0), m_inQueue(false), m_playerLoading(false), m_playerLogout(false), m_playerRecentlyLogout(false),
46 m_latency(0), m_TutorialsChanged(false)
48 if (sock)
50 m_Address = sock->GetRemoteAddress ();
51 sock->AddReference ();
55 /// WorldSession destructor
56 WorldSession::~WorldSession()
58 ///- unload player if not unloaded
59 if (_player)
60 LogoutPlayer (true);
62 /// - If have unclosed socket, close it
63 if (m_Socket)
65 m_Socket->CloseSocket ();
66 m_Socket->RemoveReference ();
67 m_Socket = NULL;
70 ///- empty incoming packet queue
71 while(!_recvQueue.empty())
73 WorldPacket *packet = _recvQueue.next ();
74 delete packet;
78 void WorldSession::SizeError(WorldPacket const& packet, uint32 size) const
80 sLog.outError("Client (account %u) send packet %s (%u) with size %u but expected %u (attempt crash server?), skipped",
81 GetAccountId(),LookupOpcodeName(packet.GetOpcode()),packet.GetOpcode(),packet.size(),size);
84 /// Get the player name
85 char const* WorldSession::GetPlayerName() const
87 return GetPlayer() ? GetPlayer()->GetName() : "<none>";
90 /// Send a packet to the client
91 void WorldSession::SendPacket(WorldPacket const* packet)
93 if (!m_Socket)
94 return;
96 #ifdef MANGOS_DEBUG
98 // Code for network use statistic
99 static uint64 sendPacketCount = 0;
100 static uint64 sendPacketBytes = 0;
102 static time_t firstTime = time(NULL);
103 static time_t lastTime = firstTime; // next 60 secs start time
105 static uint64 sendLastPacketCount = 0;
106 static uint64 sendLastPacketBytes = 0;
108 time_t cur_time = time(NULL);
110 if((cur_time - lastTime) < 60)
112 sendPacketCount+=1;
113 sendPacketBytes+=packet->size();
115 sendLastPacketCount+=1;
116 sendLastPacketBytes+=packet->size();
118 else
120 uint64 minTime = uint64(cur_time - lastTime);
121 uint64 fullTime = uint64(lastTime - firstTime);
122 sLog.outDetail("Send all time packets count: " I64FMTD " bytes: " I64FMTD " avr.count/sec: %f avr.bytes/sec: %f time: %u",sendPacketCount,sendPacketBytes,float(sendPacketCount)/fullTime,float(sendPacketBytes)/fullTime,uint32(fullTime));
123 sLog.outDetail("Send last min packets count: " I64FMTD " bytes: " I64FMTD " avr.count/sec: %f avr.bytes/sec: %f",sendLastPacketCount,sendLastPacketBytes,float(sendLastPacketCount)/minTime,float(sendLastPacketBytes)/minTime);
125 lastTime = cur_time;
126 sendLastPacketCount = 1;
127 sendLastPacketBytes = packet->wpos(); // wpos is real written size
130 #endif // !MANGOS_DEBUG
132 if (m_Socket->SendPacket (*packet) == -1)
133 m_Socket->CloseSocket ();
136 /// Add an incoming packet to the queue
137 void WorldSession::QueuePacket(WorldPacket* new_packet)
139 _recvQueue.add(new_packet);
142 /// Logging helper for unexpected opcodes
143 void WorldSession::logUnexpectedOpcode(WorldPacket* packet, const char *reason)
145 sLog.outError( "SESSION: received unexpected opcode %s (0x%.4X) %s",
146 LookupOpcodeName(packet->GetOpcode()),
147 packet->GetOpcode(),
148 reason);
151 /// Update the WorldSession (triggered by World update)
152 bool WorldSession::Update(uint32 /*diff*/)
154 ///- Retrieve packets from the receive queue and call the appropriate handlers
155 /// not proccess packets if socket already closed
156 while (!_recvQueue.empty() && m_Socket && !m_Socket->IsClosed ())
158 WorldPacket *packet = _recvQueue.next();
160 /*#if 1
161 sLog.outError( "MOEP: %s (0x%.4X)",
162 LookupOpcodeName(packet->GetOpcode()),
163 packet->GetOpcode());
164 #endif*/
166 if(packet->GetOpcode() >= NUM_MSG_TYPES)
168 sLog.outError( "SESSION: received non-existed opcode %s (0x%.4X)",
169 LookupOpcodeName(packet->GetOpcode()),
170 packet->GetOpcode());
172 else
174 OpcodeHandler& opHandle = opcodeTable[packet->GetOpcode()];
175 switch (opHandle.status)
177 case STATUS_LOGGEDIN:
178 if(!_player)
180 // skip STATUS_LOGGEDIN opcode unexpected errors if player logout sometime ago - this can be network lag delayed packets
181 if(!m_playerRecentlyLogout)
182 logUnexpectedOpcode(packet, "the player has not logged in yet");
184 else if(_player->IsInWorld())
185 (this->*opHandle.handler)(*packet);
186 // lag can cause STATUS_LOGGEDIN opcodes to arrive after the player started a transfer
187 break;
188 case STATUS_TRANSFER:
189 if(!_player)
190 logUnexpectedOpcode(packet, "the player has not logged in yet");
191 else if(_player->IsInWorld())
192 logUnexpectedOpcode(packet, "the player is still in world");
193 else
194 (this->*opHandle.handler)(*packet);
195 break;
196 case STATUS_AUTHED:
197 // prevent cheating with skip queue wait
198 if(m_inQueue)
200 logUnexpectedOpcode(packet, "the player not pass queue yet");
201 break;
204 m_playerRecentlyLogout = false;
205 (this->*opHandle.handler)(*packet);
206 break;
207 case STATUS_NEVER:
208 sLog.outError( "SESSION: received not allowed opcode %s (0x%.4X)",
209 LookupOpcodeName(packet->GetOpcode()),
210 packet->GetOpcode());
211 break;
215 delete packet;
218 ///- Cleanup socket pointer if need
219 if (m_Socket && m_Socket->IsClosed ())
221 m_Socket->RemoveReference ();
222 m_Socket = NULL;
225 ///- If necessary, log the player out
226 time_t currTime = time(NULL);
227 if (!m_Socket || (ShouldLogOut(currTime) && !m_playerLoading))
228 LogoutPlayer(true);
230 if (!m_Socket)
231 return false; //Will remove this session from the world session map
233 return true;
236 /// %Log the player out
237 void WorldSession::LogoutPlayer(bool Save)
239 // finish pending transfers before starting the logout
240 while(_player && _player->IsBeingTeleportedFar())
241 HandleMoveWorldportAckOpcode();
243 m_playerLogout = true;
245 if (_player)
247 if (uint64 lguid = GetPlayer()->GetLootGUID())
248 DoLootRelease(lguid);
250 ///- If the player just died before logging out, make him appear as a ghost
251 //FIXME: logout must be delayed in case lost connection with client in time of combat
252 if (_player->GetDeathTimer())
254 _player->getHostilRefManager().deleteReferences();
255 _player->BuildPlayerRepop();
256 _player->RepopAtGraveyard();
258 else if (!_player->getAttackers().empty())
260 _player->CombatStop();
261 _player->getHostilRefManager().setOnlineOfflineState(false);
262 _player->RemoveAllAurasOnDeath();
264 // build set of player who attack _player or who have pet attacking of _player
265 std::set<Player*> aset;
266 for(Unit::AttackerSet::const_iterator itr = _player->getAttackers().begin(); itr != _player->getAttackers().end(); ++itr)
268 Unit* owner = (*itr)->GetOwner(); // including player controlled case
269 if(owner)
271 if(owner->GetTypeId()==TYPEID_PLAYER)
272 aset.insert((Player*)owner);
274 else
275 if((*itr)->GetTypeId()==TYPEID_PLAYER)
276 aset.insert((Player*)(*itr));
279 _player->SetPvPDeath(!aset.empty());
280 _player->KillPlayer();
281 _player->BuildPlayerRepop();
282 _player->RepopAtGraveyard();
284 // give honor to all attackers from set like group case
285 for(std::set<Player*>::const_iterator itr = aset.begin(); itr != aset.end(); ++itr)
286 (*itr)->RewardHonor(_player,aset.size());
288 // give bg rewards and update counters like kill by first from attackers
289 // this can't be called for all attackers.
290 if(!aset.empty())
291 if(BattleGround *bg = _player->GetBattleGround())
292 bg->HandleKillPlayer(_player,*aset.begin());
294 else if(_player->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION))
296 // this will kill character by SPELL_AURA_SPIRIT_OF_REDEMPTION
297 _player->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT);
298 //_player->SetDeathPvP(*); set at SPELL_AURA_SPIRIT_OF_REDEMPTION apply time
299 _player->KillPlayer();
300 _player->BuildPlayerRepop();
301 _player->RepopAtGraveyard();
303 //drop a flag if player is carrying it
304 if(BattleGround *bg = _player->GetBattleGround())
305 bg->EventPlayerLoggedOut(_player);
307 ///- Teleport to home if the player is in an invalid instance
308 if(!_player->m_InstanceValid && !_player->isGameMaster())
309 _player->TeleportTo(_player->m_homebindMapId, _player->m_homebindX, _player->m_homebindY, _player->m_homebindZ, _player->GetOrientation());
311 for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i)
313 if(BattleGroundQueueTypeId bgQueueTypeId = _player->GetBattleGroundQueueTypeId(i))
315 _player->RemoveBattleGroundQueueId(bgQueueTypeId);
316 sBattleGroundMgr.m_BattleGroundQueues[ bgQueueTypeId ].RemovePlayer(_player->GetGUID(), true);
320 ///- Reset the online field in the account table
321 // no point resetting online in character table here as Player::SaveToDB() will set it to 1 since player has not been removed from world at this stage
322 //No SQL injection as AccountID is uint32
323 loginDatabase.PExecute("UPDATE account SET online = 0 WHERE id = '%u'", GetAccountId());
325 ///- If the player is in a guild, update the guild roster and broadcast a logout message to other guild members
326 Guild *guild = objmgr.GetGuildById(_player->GetGuildId());
327 if(guild)
329 guild->LoadPlayerStatsByGuid(_player->GetGUID());
330 guild->UpdateLogoutTime(_player->GetGUID());
332 WorldPacket data(SMSG_GUILD_EVENT, (1+1+12+8)); // name limited to 12 in character table.
333 data<<(uint8)GE_SIGNED_OFF;
334 data<<(uint8)1;
335 data<<_player->GetName();
336 data<<_player->GetGUID();
337 guild->BroadcastPacket(&data);
340 ///- Remove pet
341 _player->RemovePet(NULL,PET_SAVE_AS_CURRENT, true);
343 ///- empty buyback items and save the player in the database
344 // some save parts only correctly work in case player present in map/player_lists (pets, etc)
345 if(Save)
347 uint32 eslot;
348 for(int j = BUYBACK_SLOT_START; j < BUYBACK_SLOT_END; ++j)
350 eslot = j - BUYBACK_SLOT_START;
351 _player->SetUInt64Value(PLAYER_FIELD_VENDORBUYBACK_SLOT_1 + (eslot * 2), 0);
352 _player->SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1 + eslot, 0);
353 _player->SetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1 + eslot, 0);
355 _player->SaveToDB();
358 ///- Leave all channels before player delete...
359 _player->CleanupChannels();
361 ///- If the player is in a group (or invited), remove him. If the group if then only 1 person, disband the group.
362 _player->UninviteFromGroup();
364 // remove player from the group if he is:
365 // a) in group; b) not in raid group; c) logging out normally (not being kicked or disconnected)
366 if(_player->GetGroup() && !_player->GetGroup()->isRaidGroup() && m_Socket)
367 _player->RemoveFromGroup();
369 ///- Remove the player from the world
370 // the player may not be in the world when logging out
371 // e.g if he got disconnected during a transfer to another map
372 // calls to GetMap in this case may cause crashes
373 if(_player->IsInWorld()) _player->GetMap()->Remove(_player, false);
374 // RemoveFromWorld does cleanup that requires the player to be in the accessor
375 ObjectAccessor::Instance().RemoveObject(_player);
377 ///- Send update to group
378 if(_player->GetGroup())
379 _player->GetGroup()->SendUpdate();
381 ///- Broadcast a logout message to the player's friends
382 sSocialMgr.SendFriendStatus(_player, FRIEND_OFFLINE, _player->GetGUIDLow(), true);
384 ///- Delete the player object
385 _player->CleanupsBeforeDelete(); // do some cleanup before deleting to prevent crash at crossreferences to already deleted data
387 sSocialMgr.RemovePlayerSocial (_player->GetGUIDLow ());
388 delete _player;
389 _player = NULL;
391 ///- Send the 'logout complete' packet to the client
392 WorldPacket data( SMSG_LOGOUT_COMPLETE, 0 );
393 SendPacket( &data );
395 ///- Since each account can only have one online character at any given time, ensure all characters for active account are marked as offline
396 //No SQL injection as AccountId is uint32
397 CharacterDatabase.PExecute("UPDATE characters SET online = 0 WHERE account = '%u'",
398 GetAccountId());
399 sLog.outDebug( "SESSION: Sent SMSG_LOGOUT_COMPLETE Message" );
402 m_playerLogout = false;
403 m_playerRecentlyLogout = true;
404 LogoutRequest(0);
407 /// Kick a player out of the World
408 void WorldSession::KickPlayer()
410 if (m_Socket)
411 m_Socket->CloseSocket ();
414 /// Cancel channeling handler
416 void WorldSession::SendAreaTriggerMessage(const char* Text, ...)
418 va_list ap;
419 char szStr [1024];
420 szStr[0] = '\0';
422 va_start(ap, Text);
423 vsnprintf( szStr, 1024, Text, ap );
424 va_end(ap);
426 uint32 length = strlen(szStr)+1;
427 WorldPacket data(SMSG_AREA_TRIGGER_MESSAGE, 4+length);
428 data << length;
429 data << szStr;
430 SendPacket(&data);
433 void WorldSession::SendNotification(const char *format,...)
435 if(format)
437 va_list ap;
438 char szStr [1024];
439 szStr[0] = '\0';
440 va_start(ap, format);
441 vsnprintf( szStr, 1024, format, ap );
442 va_end(ap);
444 WorldPacket data(SMSG_NOTIFICATION, (strlen(szStr)+1));
445 data << szStr;
446 SendPacket(&data);
450 void WorldSession::SendNotification(int32 string_id,...)
452 char const* format = GetMangosString(string_id);
453 if(format)
455 va_list ap;
456 char szStr [1024];
457 szStr[0] = '\0';
458 va_start(ap, string_id);
459 vsnprintf( szStr, 1024, format, ap );
460 va_end(ap);
462 WorldPacket data(SMSG_NOTIFICATION, (strlen(szStr)+1));
463 data << szStr;
464 SendPacket(&data);
468 void WorldSession::SendSetPhaseShift(uint32 PhaseShift)
470 WorldPacket data(SMSG_SET_PHASE_SHIFT, 4);
471 data << uint32(PhaseShift);
472 SendPacket(&data);
475 const char * WorldSession::GetMangosString( int32 entry ) const
477 return objmgr.GetMangosString(entry,GetSessionDbLocaleIndex());
480 void WorldSession::Handle_NULL( WorldPacket& recvPacket )
482 sLog.outError( "SESSION: received unhandled opcode %s (0x%.4X)",
483 LookupOpcodeName(recvPacket.GetOpcode()),
484 recvPacket.GetOpcode());
487 void WorldSession::Handle_EarlyProccess( WorldPacket& recvPacket )
489 sLog.outError( "SESSION: received opcode %s (0x%.4X) that must be processed in WorldSocket::OnRead",
490 LookupOpcodeName(recvPacket.GetOpcode()),
491 recvPacket.GetOpcode());
494 void WorldSession::Handle_ServerSide( WorldPacket& recvPacket )
496 sLog.outError( "SESSION: received server-side opcode %s (0x%.4X)",
497 LookupOpcodeName(recvPacket.GetOpcode()),
498 recvPacket.GetOpcode());
501 void WorldSession::Handle_Deprecated( WorldPacket& recvPacket )
503 sLog.outError( "SESSION: received deprecated opcode %s (0x%.4X)",
504 LookupOpcodeName(recvPacket.GetOpcode()),
505 recvPacket.GetOpcode());
508 void WorldSession::SendAuthWaitQue(uint32 position)
510 if(position == 0)
512 WorldPacket packet( SMSG_AUTH_RESPONSE, 1 );
513 packet << uint8( AUTH_OK );
514 SendPacket(&packet);
516 else
518 WorldPacket packet( SMSG_AUTH_RESPONSE, 5 );
519 packet << uint8( AUTH_WAIT_QUEUE );
520 packet << uint32 (position);
521 SendPacket(&packet);
525 void WorldSession::LoadAccountData()
527 for (uint32 i = 0; i < NUM_ACCOUNT_DATA_TYPES; ++i)
529 AccountData data;
530 m_accountData[i] = data;
533 QueryResult *result = CharacterDatabase.PQuery("SELECT type, time, data FROM account_data WHERE account='%u'", GetAccountId());
535 if(!result)
536 return;
540 Field *fields = result->Fetch();
542 uint32 type = fields[0].GetUInt32();
543 if(type < NUM_ACCOUNT_DATA_TYPES)
545 m_accountData[type].Time = fields[1].GetUInt32();
546 m_accountData[type].Data = fields[2].GetCppString();
548 } while (result->NextRow());
550 delete result;
553 void WorldSession::SetAccountData(uint32 type, time_t time_, std::string data)
555 m_accountData[type].Time = time_;
556 m_accountData[type].Data = data;
558 uint32 acc = GetAccountId();
560 CharacterDatabase.BeginTransaction ();
561 CharacterDatabase.PExecute("DELETE FROM account_data WHERE account='%u' AND type='%u'", acc, type);
562 CharacterDatabase.escape_string(data);
563 CharacterDatabase.PExecute("INSERT INTO account_data VALUES ('%u','%u','%u','%s')", acc, type, (uint32)time_, data.c_str());
564 CharacterDatabase.CommitTransaction ();
567 void WorldSession::LoadTutorialsData()
569 for ( int aX = 0 ; aX < 8 ; ++aX )
570 m_Tutorials[ aX ] = 0;
572 QueryResult *result = CharacterDatabase.PQuery("SELECT tut0,tut1,tut2,tut3,tut4,tut5,tut6,tut7 FROM character_tutorial WHERE account = '%u'", GetAccountId());
574 if(result)
578 Field *fields = result->Fetch();
580 for (int iI = 0; iI < 8; ++iI)
581 m_Tutorials[iI] = fields[iI].GetUInt32();
583 while( result->NextRow() );
585 delete result;
588 m_TutorialsChanged = false;
591 void WorldSession::SendTutorialsData()
593 WorldPacket data(SMSG_TUTORIAL_FLAGS, 4*8);
594 for(uint32 i = 0; i < 8; ++i)
595 data << m_Tutorials[i];
596 SendPacket(&data);
599 void WorldSession::SaveTutorialsData()
601 if(!m_TutorialsChanged)
602 return;
604 uint32 Rows=0;
605 // it's better than rebuilding indexes multiple times
606 QueryResult *result = CharacterDatabase.PQuery("SELECT count(*) AS r FROM character_tutorial WHERE account = '%u'", GetAccountId());
607 if(result)
609 Rows = result->Fetch()[0].GetUInt32();
610 delete result;
613 if (Rows)
615 CharacterDatabase.PExecute("UPDATE character_tutorial SET tut0='%u', tut1='%u', tut2='%u', tut3='%u', tut4='%u', tut5='%u', tut6='%u', tut7='%u' WHERE account = '%u'",
616 m_Tutorials[0], m_Tutorials[1], m_Tutorials[2], m_Tutorials[3], m_Tutorials[4], m_Tutorials[5], m_Tutorials[6], m_Tutorials[7], GetAccountId());
618 else
620 CharacterDatabase.PExecute("INSERT INTO character_tutorial (account,tut0,tut1,tut2,tut3,tut4,tut5,tut6,tut7) VALUES ('%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u')", GetAccountId(), m_Tutorials[0], m_Tutorials[1], m_Tutorials[2], m_Tutorials[3], m_Tutorials[4], m_Tutorials[5], m_Tutorials[6], m_Tutorials[7]);
623 m_TutorialsChanged = false;
626 void WorldSession::ReadMovementInfo(WorldPacket &data, MovementInfo *mi)
628 CHECK_PACKET_SIZE(data, data.rpos()+4+2+4+4+4+4+4);
629 data >> mi->flags;
630 data >> mi->unk1;
631 data >> mi->time;
632 data >> mi->x;
633 data >> mi->y;
634 data >> mi->z;
635 data >> mi->o;
637 if(mi->flags & MOVEMENTFLAG_ONTRANSPORT)
639 if(!data.readPackGUID(mi->t_guid))
640 return;
642 CHECK_PACKET_SIZE(data, data.rpos()+4+4+4+4+4+1);
643 data >> mi->t_x;
644 data >> mi->t_y;
645 data >> mi->t_z;
646 data >> mi->t_o;
647 data >> mi->t_time;
648 data >> mi->t_seat;
651 if((mi->flags & (MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_FLYING2)) || (mi->unk1 & 0x20))
653 CHECK_PACKET_SIZE(data, data.rpos()+4);
654 data >> mi->s_pitch;
657 CHECK_PACKET_SIZE(data, data.rpos()+4);
658 data >> mi->fallTime;
660 if(mi->flags & MOVEMENTFLAG_JUMPING)
662 CHECK_PACKET_SIZE(data, data.rpos()+4+4+4+4);
663 data >> mi->j_unk;
664 data >> mi->j_sinAngle;
665 data >> mi->j_cosAngle;
666 data >> mi->j_xyspeed;
669 if(mi->flags & MOVEMENTFLAG_SPLINE)
671 CHECK_PACKET_SIZE(data, data.rpos()+4);
672 data >> mi->u_unk1;
676 void WorldSession::ReadAddonsInfo(WorldPacket &data)
678 if (data.rpos() + 4 > data.size())
679 return;
680 uint32 size;
681 data >> size;
683 if(!size)
684 return;
686 uLongf uSize = size;
688 uint32 pos = data.rpos();
690 ByteBuffer addonInfo;
691 addonInfo.resize(size);
693 if (uncompress(const_cast<uint8*>(addonInfo.contents()), &uSize, const_cast<uint8*>(data.contents() + pos), data.size() - pos) == Z_OK)
695 uint32 addonsCount;
696 addonInfo >> addonsCount; // addons count
698 for(uint32 i = 0; i < addonsCount; ++i)
700 std::string addonName;
701 uint8 enabled;
702 uint32 crc, unk1;
704 // check next addon data format correctness
705 if(addonInfo.rpos()+1 > addonInfo.size())
706 return;
708 addonInfo >> addonName;
710 // recheck next addon data format correctness
711 if(addonInfo.rpos()+1+4+4 > addonInfo.size())
712 return;
714 addonInfo >> enabled >> crc >> unk1;
716 sLog.outDebug("ADDON: Name: %s, Enabled: 0x%x, CRC: 0x%x, Unknown2: 0x%x", addonName.c_str(), enabled, crc, unk1);
718 m_addonsList.push_back(AddonInfo(addonName, enabled, crc));
721 uint32 unk2;
722 addonInfo >> unk2;
724 if(addonInfo.rpos() != addonInfo.size())
725 sLog.outDebug("packet under read!");
727 else
728 sLog.outError("Addon packet uncompress error!");
731 void WorldSession::SendAddonsInfo()
733 unsigned char tdata[256] =
735 0xC3, 0x5B, 0x50, 0x84, 0xB9, 0x3E, 0x32, 0x42, 0x8C, 0xD0, 0xC7, 0x48, 0xFA, 0x0E, 0x5D, 0x54,
736 0x5A, 0xA3, 0x0E, 0x14, 0xBA, 0x9E, 0x0D, 0xB9, 0x5D, 0x8B, 0xEE, 0xB6, 0x84, 0x93, 0x45, 0x75,
737 0xFF, 0x31, 0xFE, 0x2F, 0x64, 0x3F, 0x3D, 0x6D, 0x07, 0xD9, 0x44, 0x9B, 0x40, 0x85, 0x59, 0x34,
738 0x4E, 0x10, 0xE1, 0xE7, 0x43, 0x69, 0xEF, 0x7C, 0x16, 0xFC, 0xB4, 0xED, 0x1B, 0x95, 0x28, 0xA8,
739 0x23, 0x76, 0x51, 0x31, 0x57, 0x30, 0x2B, 0x79, 0x08, 0x50, 0x10, 0x1C, 0x4A, 0x1A, 0x2C, 0xC8,
740 0x8B, 0x8F, 0x05, 0x2D, 0x22, 0x3D, 0xDB, 0x5A, 0x24, 0x7A, 0x0F, 0x13, 0x50, 0x37, 0x8F, 0x5A,
741 0xCC, 0x9E, 0x04, 0x44, 0x0E, 0x87, 0x01, 0xD4, 0xA3, 0x15, 0x94, 0x16, 0x34, 0xC6, 0xC2, 0xC3,
742 0xFB, 0x49, 0xFE, 0xE1, 0xF9, 0xDA, 0x8C, 0x50, 0x3C, 0xBE, 0x2C, 0xBB, 0x57, 0xED, 0x46, 0xB9,
743 0xAD, 0x8B, 0xC6, 0xDF, 0x0E, 0xD6, 0x0F, 0xBE, 0x80, 0xB3, 0x8B, 0x1E, 0x77, 0xCF, 0xAD, 0x22,
744 0xCF, 0xB7, 0x4B, 0xCF, 0xFB, 0xF0, 0x6B, 0x11, 0x45, 0x2D, 0x7A, 0x81, 0x18, 0xF2, 0x92, 0x7E,
745 0x98, 0x56, 0x5D, 0x5E, 0x69, 0x72, 0x0A, 0x0D, 0x03, 0x0A, 0x85, 0xA2, 0x85, 0x9C, 0xCB, 0xFB,
746 0x56, 0x6E, 0x8F, 0x44, 0xBB, 0x8F, 0x02, 0x22, 0x68, 0x63, 0x97, 0xBC, 0x85, 0xBA, 0xA8, 0xF7,
747 0xB5, 0x40, 0x68, 0x3C, 0x77, 0x86, 0x6F, 0x4B, 0xD7, 0x88, 0xCA, 0x8A, 0xD7, 0xCE, 0x36, 0xF0,
748 0x45, 0x6E, 0xD5, 0x64, 0x79, 0x0F, 0x17, 0xFC, 0x64, 0xDD, 0x10, 0x6F, 0xF3, 0xF5, 0xE0, 0xA6,
749 0xC3, 0xFB, 0x1B, 0x8C, 0x29, 0xEF, 0x8E, 0xE5, 0x34, 0xCB, 0xD1, 0x2A, 0xCE, 0x79, 0xC3, 0x9A,
750 0x0D, 0x36, 0xEA, 0x01, 0xE0, 0xAA, 0x91, 0x20, 0x54, 0xF0, 0x72, 0xD8, 0x1E, 0xC7, 0x89, 0xD2
753 WorldPacket data(SMSG_ADDON_INFO, 4);
755 for(AddonsList::iterator itr = m_addonsList.begin(); itr != m_addonsList.end(); ++itr)
757 uint8 state = 2; // 2 is sent here
758 data << uint8(state);
760 uint8 unk1 = 1; // 1 is sent here
761 data << uint8(unk1);
762 if (unk1)
764 uint8 unk2 = (itr->CRC != 0x4c1c776d); // If addon is Standard addon CRC
765 data << uint8(unk2);
766 if (unk2) // if CRC is wrong, add public key (client need it)
767 data.append(tdata, sizeof(tdata));
769 data << uint32(0);
772 uint8 unk3 = 0; // 0 is sent here
773 data << uint8(unk3);
774 if (unk3)
776 // String, 256 (null terminated?)
777 data << uint8(0);
781 m_addonsList.clear();
783 uint32 count = 0;
784 data << uint32(count);
785 /*for(uint32 i = 0; i < count; ++i)
787 uint32
788 string (16 bytes)
789 string (16 bytes)
790 uint32
793 SendPacket(&data);