[7921] Set pet and for creature owner in Spell::EffectSummon similar other summon...
[getmangos.git] / src / game / WorldSession.cpp
blob8e320bc777b88fa1915a8bc31fc48e8408a8aa00
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), m_latency(0)
47 if (sock)
49 m_Address = sock->GetRemoteAddress ();
50 sock->AddReference ();
54 /// WorldSession destructor
55 WorldSession::~WorldSession()
57 ///- unload player if not unloaded
58 if (_player)
59 LogoutPlayer (true);
61 /// - If have unclosed socket, close it
62 if (m_Socket)
64 m_Socket->CloseSocket ();
65 m_Socket->RemoveReference ();
66 m_Socket = NULL;
69 ///- empty incoming packet queue
70 while(!_recvQueue.empty())
72 WorldPacket *packet = _recvQueue.next ();
73 delete packet;
77 void WorldSession::SizeError(WorldPacket const& packet, uint32 size) const
79 sLog.outError("Client (account %u) send packet %s (%u) with size %u but expected %u (attempt crash server?), skipped",
80 GetAccountId(),LookupOpcodeName(packet.GetOpcode()),packet.GetOpcode(),packet.size(),size);
83 /// Get the player name
84 char const* WorldSession::GetPlayerName() const
86 return GetPlayer() ? GetPlayer()->GetName() : "<none>";
89 /// Send a packet to the client
90 void WorldSession::SendPacket(WorldPacket const* packet)
92 if (!m_Socket)
93 return;
95 #ifdef MANGOS_DEBUG
97 // Code for network use statistic
98 static uint64 sendPacketCount = 0;
99 static uint64 sendPacketBytes = 0;
101 static time_t firstTime = time(NULL);
102 static time_t lastTime = firstTime; // next 60 secs start time
104 static uint64 sendLastPacketCount = 0;
105 static uint64 sendLastPacketBytes = 0;
107 time_t cur_time = time(NULL);
109 if((cur_time - lastTime) < 60)
111 sendPacketCount+=1;
112 sendPacketBytes+=packet->size();
114 sendLastPacketCount+=1;
115 sendLastPacketBytes+=packet->size();
117 else
119 uint64 minTime = uint64(cur_time - lastTime);
120 uint64 fullTime = uint64(lastTime - firstTime);
121 sLog.outDetail("Send all time packets count: " UI64FMTD " bytes: " UI64FMTD " avr.count/sec: %f avr.bytes/sec: %f time: %u",sendPacketCount,sendPacketBytes,float(sendPacketCount)/fullTime,float(sendPacketBytes)/fullTime,uint32(fullTime));
122 sLog.outDetail("Send last min packets count: " UI64FMTD " bytes: " UI64FMTD " avr.count/sec: %f avr.bytes/sec: %f",sendLastPacketCount,sendLastPacketBytes,float(sendLastPacketCount)/minTime,float(sendLastPacketBytes)/minTime);
124 lastTime = cur_time;
125 sendLastPacketCount = 1;
126 sendLastPacketBytes = packet->wpos(); // wpos is real written size
129 #endif // !MANGOS_DEBUG
131 if (m_Socket->SendPacket (*packet) == -1)
132 m_Socket->CloseSocket ();
135 /// Add an incoming packet to the queue
136 void WorldSession::QueuePacket(WorldPacket* new_packet)
138 _recvQueue.add(new_packet);
141 /// Logging helper for unexpected opcodes
142 void WorldSession::logUnexpectedOpcode(WorldPacket* packet, const char *reason)
144 sLog.outError( "SESSION: received unexpected opcode %s (0x%.4X) %s",
145 LookupOpcodeName(packet->GetOpcode()),
146 packet->GetOpcode(),
147 reason);
150 /// Update the WorldSession (triggered by World update)
151 bool WorldSession::Update(uint32 /*diff*/)
153 ///- Retrieve packets from the receive queue and call the appropriate handlers
154 /// not proccess packets if socket already closed
155 while (!_recvQueue.empty() && m_Socket && !m_Socket->IsClosed ())
157 WorldPacket *packet = _recvQueue.next();
159 /*#if 1
160 sLog.outError( "MOEP: %s (0x%.4X)",
161 LookupOpcodeName(packet->GetOpcode()),
162 packet->GetOpcode());
163 #endif*/
165 if(packet->GetOpcode() >= NUM_MSG_TYPES)
167 sLog.outError( "SESSION: received non-existed opcode %s (0x%.4X)",
168 LookupOpcodeName(packet->GetOpcode()),
169 packet->GetOpcode());
171 else
173 OpcodeHandler& opHandle = opcodeTable[packet->GetOpcode()];
174 switch (opHandle.status)
176 case STATUS_LOGGEDIN:
177 if(!_player)
179 // skip STATUS_LOGGEDIN opcode unexpected errors if player logout sometime ago - this can be network lag delayed packets
180 if(!m_playerRecentlyLogout)
181 logUnexpectedOpcode(packet, "the player has not logged in yet");
183 else if(_player->IsInWorld())
184 (this->*opHandle.handler)(*packet);
185 // lag can cause STATUS_LOGGEDIN opcodes to arrive after the player started a transfer
186 break;
187 case STATUS_TRANSFER:
188 if(!_player)
189 logUnexpectedOpcode(packet, "the player has not logged in yet");
190 else if(_player->IsInWorld())
191 logUnexpectedOpcode(packet, "the player is still in world");
192 else
193 (this->*opHandle.handler)(*packet);
194 break;
195 case STATUS_AUTHED:
196 // prevent cheating with skip queue wait
197 if(m_inQueue)
199 logUnexpectedOpcode(packet, "the player not pass queue yet");
200 break;
203 m_playerRecentlyLogout = false;
204 (this->*opHandle.handler)(*packet);
205 break;
206 case STATUS_NEVER:
207 sLog.outError( "SESSION: received not allowed opcode %s (0x%.4X)",
208 LookupOpcodeName(packet->GetOpcode()),
209 packet->GetOpcode());
210 break;
214 delete packet;
217 ///- Cleanup socket pointer if need
218 if (m_Socket && m_Socket->IsClosed ())
220 m_Socket->RemoveReference ();
221 m_Socket = NULL;
224 ///- If necessary, log the player out
225 time_t currTime = time(NULL);
226 if (!m_Socket || (ShouldLogOut(currTime) && !m_playerLoading))
227 LogoutPlayer(true);
229 if (!m_Socket)
230 return false; //Will remove this session from the world session map
232 return true;
235 /// %Log the player out
236 void WorldSession::LogoutPlayer(bool Save)
238 // finish pending transfers before starting the logout
239 while(_player && _player->IsBeingTeleportedFar())
240 HandleMoveWorldportAckOpcode();
242 m_playerLogout = true;
244 if (_player)
246 if (uint64 lguid = GetPlayer()->GetLootGUID())
247 DoLootRelease(lguid);
249 ///- If the player just died before logging out, make him appear as a ghost
250 //FIXME: logout must be delayed in case lost connection with client in time of combat
251 if (_player->GetDeathTimer())
253 _player->getHostilRefManager().deleteReferences();
254 _player->BuildPlayerRepop();
255 _player->RepopAtGraveyard();
257 else if (!_player->getAttackers().empty())
259 _player->CombatStop();
260 _player->getHostilRefManager().setOnlineOfflineState(false);
261 _player->RemoveAllAurasOnDeath();
263 // build set of player who attack _player or who have pet attacking of _player
264 std::set<Player*> aset;
265 for(Unit::AttackerSet::const_iterator itr = _player->getAttackers().begin(); itr != _player->getAttackers().end(); ++itr)
267 Unit* owner = (*itr)->GetOwner(); // including player controlled case
268 if(owner)
270 if(owner->GetTypeId()==TYPEID_PLAYER)
271 aset.insert((Player*)owner);
273 else
274 if((*itr)->GetTypeId()==TYPEID_PLAYER)
275 aset.insert((Player*)(*itr));
278 _player->SetPvPDeath(!aset.empty());
279 _player->KillPlayer();
280 _player->BuildPlayerRepop();
281 _player->RepopAtGraveyard();
283 // give honor to all attackers from set like group case
284 for(std::set<Player*>::const_iterator itr = aset.begin(); itr != aset.end(); ++itr)
285 (*itr)->RewardHonor(_player,aset.size());
287 // give bg rewards and update counters like kill by first from attackers
288 // this can't be called for all attackers.
289 if(!aset.empty())
290 if(BattleGround *bg = _player->GetBattleGround())
291 bg->HandleKillPlayer(_player,*aset.begin());
293 else if(_player->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION))
295 // this will kill character by SPELL_AURA_SPIRIT_OF_REDEMPTION
296 _player->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT);
297 //_player->SetDeathPvP(*); set at SPELL_AURA_SPIRIT_OF_REDEMPTION apply time
298 _player->KillPlayer();
299 _player->BuildPlayerRepop();
300 _player->RepopAtGraveyard();
302 //drop a flag if player is carrying it
303 if(BattleGround *bg = _player->GetBattleGround())
304 bg->EventPlayerLoggedOut(_player);
306 ///- Teleport to home if the player is in an invalid instance
307 if(!_player->m_InstanceValid && !_player->isGameMaster())
308 _player->TeleportTo(_player->m_homebindMapId, _player->m_homebindX, _player->m_homebindY, _player->m_homebindZ, _player->GetOrientation());
310 for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i)
312 if(BattleGroundQueueTypeId bgQueueTypeId = _player->GetBattleGroundQueueTypeId(i))
314 _player->RemoveBattleGroundQueueId(bgQueueTypeId);
315 sBattleGroundMgr.m_BattleGroundQueues[ bgQueueTypeId ].RemovePlayer(_player->GetGUID(), true);
319 ///- Reset the online field in the account table
320 // 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
321 //No SQL injection as AccountID is uint32
322 loginDatabase.PExecute("UPDATE account SET online = 0 WHERE id = '%u'", GetAccountId());
324 ///- If the player is in a guild, update the guild roster and broadcast a logout message to other guild members
325 Guild *guild = objmgr.GetGuildById(_player->GetGuildId());
326 if(guild)
328 guild->LoadPlayerStatsByGuid(_player->GetGUID());
329 guild->UpdateLogoutTime(_player->GetGUID());
331 WorldPacket data(SMSG_GUILD_EVENT, (1+1+12+8)); // name limited to 12 in character table.
332 data<<(uint8)GE_SIGNED_OFF;
333 data<<(uint8)1;
334 data<<_player->GetName();
335 data<<_player->GetGUID();
336 guild->BroadcastPacket(&data);
339 ///- Remove pet
340 _player->RemovePet(NULL,PET_SAVE_AS_CURRENT, true);
342 ///- empty buyback items and save the player in the database
343 // some save parts only correctly work in case player present in map/player_lists (pets, etc)
344 if(Save)
346 uint32 eslot;
347 for(int j = BUYBACK_SLOT_START; j < BUYBACK_SLOT_END; ++j)
349 eslot = j - BUYBACK_SLOT_START;
350 _player->SetUInt64Value(PLAYER_FIELD_VENDORBUYBACK_SLOT_1 + (eslot * 2), 0);
351 _player->SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1 + eslot, 0);
352 _player->SetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1 + eslot, 0);
354 _player->SaveToDB();
357 ///- Leave all channels before player delete...
358 _player->CleanupChannels();
360 ///- If the player is in a group (or invited), remove him. If the group if then only 1 person, disband the group.
361 _player->UninviteFromGroup();
363 // remove player from the group if he is:
364 // a) in group; b) not in raid group; c) logging out normally (not being kicked or disconnected)
365 if(_player->GetGroup() && !_player->GetGroup()->isRaidGroup() && m_Socket)
366 _player->RemoveFromGroup();
368 ///- Remove the player from the world
369 // the player may not be in the world when logging out
370 // e.g if he got disconnected during a transfer to another map
371 // calls to GetMap in this case may cause crashes
372 if(_player->IsInWorld()) _player->GetMap()->Remove(_player, false);
373 // RemoveFromWorld does cleanup that requires the player to be in the accessor
374 ObjectAccessor::Instance().RemoveObject(_player);
376 ///- Send update to group
377 if(_player->GetGroup())
378 _player->GetGroup()->SendUpdate();
380 ///- Broadcast a logout message to the player's friends
381 sSocialMgr.SendFriendStatus(_player, FRIEND_OFFLINE, _player->GetGUIDLow(), true);
383 ///- Delete the player object
384 _player->CleanupsBeforeDelete(); // do some cleanup before deleting to prevent crash at crossreferences to already deleted data
386 sSocialMgr.RemovePlayerSocial (_player->GetGUIDLow ());
387 delete _player;
388 _player = NULL;
390 ///- Send the 'logout complete' packet to the client
391 WorldPacket data( SMSG_LOGOUT_COMPLETE, 0 );
392 SendPacket( &data );
394 ///- Since each account can only have one online character at any given time, ensure all characters for active account are marked as offline
395 //No SQL injection as AccountId is uint32
396 CharacterDatabase.PExecute("UPDATE characters SET online = 0 WHERE account = '%u'",
397 GetAccountId());
398 sLog.outDebug( "SESSION: Sent SMSG_LOGOUT_COMPLETE Message" );
401 m_playerLogout = false;
402 m_playerRecentlyLogout = true;
403 LogoutRequest(0);
406 /// Kick a player out of the World
407 void WorldSession::KickPlayer()
409 if (m_Socket)
410 m_Socket->CloseSocket ();
413 /// Cancel channeling handler
415 void WorldSession::SendAreaTriggerMessage(const char* Text, ...)
417 va_list ap;
418 char szStr [1024];
419 szStr[0] = '\0';
421 va_start(ap, Text);
422 vsnprintf( szStr, 1024, Text, ap );
423 va_end(ap);
425 uint32 length = strlen(szStr)+1;
426 WorldPacket data(SMSG_AREA_TRIGGER_MESSAGE, 4+length);
427 data << length;
428 data << szStr;
429 SendPacket(&data);
432 void WorldSession::SendNotification(const char *format,...)
434 if(format)
436 va_list ap;
437 char szStr [1024];
438 szStr[0] = '\0';
439 va_start(ap, format);
440 vsnprintf( szStr, 1024, format, ap );
441 va_end(ap);
443 WorldPacket data(SMSG_NOTIFICATION, (strlen(szStr)+1));
444 data << szStr;
445 SendPacket(&data);
449 void WorldSession::SendNotification(int32 string_id,...)
451 char const* format = GetMangosString(string_id);
452 if(format)
454 va_list ap;
455 char szStr [1024];
456 szStr[0] = '\0';
457 va_start(ap, string_id);
458 vsnprintf( szStr, 1024, format, ap );
459 va_end(ap);
461 WorldPacket data(SMSG_NOTIFICATION, (strlen(szStr)+1));
462 data << szStr;
463 SendPacket(&data);
467 void WorldSession::SendSetPhaseShift(uint32 PhaseShift)
469 WorldPacket data(SMSG_SET_PHASE_SHIFT, 4);
470 data << uint32(PhaseShift);
471 SendPacket(&data);
474 const char * WorldSession::GetMangosString( int32 entry ) const
476 return objmgr.GetMangosString(entry,GetSessionDbLocaleIndex());
479 void WorldSession::Handle_NULL( WorldPacket& recvPacket )
481 sLog.outError( "SESSION: received unhandled opcode %s (0x%.4X)",
482 LookupOpcodeName(recvPacket.GetOpcode()),
483 recvPacket.GetOpcode());
486 void WorldSession::Handle_EarlyProccess( WorldPacket& recvPacket )
488 sLog.outError( "SESSION: received opcode %s (0x%.4X) that must be processed in WorldSocket::OnRead",
489 LookupOpcodeName(recvPacket.GetOpcode()),
490 recvPacket.GetOpcode());
493 void WorldSession::Handle_ServerSide( WorldPacket& recvPacket )
495 sLog.outError( "SESSION: received server-side opcode %s (0x%.4X)",
496 LookupOpcodeName(recvPacket.GetOpcode()),
497 recvPacket.GetOpcode());
500 void WorldSession::Handle_Deprecated( WorldPacket& recvPacket )
502 sLog.outError( "SESSION: received deprecated opcode %s (0x%.4X)",
503 LookupOpcodeName(recvPacket.GetOpcode()),
504 recvPacket.GetOpcode());
507 void WorldSession::SendAuthWaitQue(uint32 position)
509 if(position == 0)
511 WorldPacket packet( SMSG_AUTH_RESPONSE, 1 );
512 packet << uint8( AUTH_OK );
513 SendPacket(&packet);
515 else
517 WorldPacket packet( SMSG_AUTH_RESPONSE, 5 );
518 packet << uint8( AUTH_WAIT_QUEUE );
519 packet << uint32 (position);
520 SendPacket(&packet);
524 void WorldSession::LoadAccountData()
526 for (uint32 i = 0; i < NUM_ACCOUNT_DATA_TYPES; ++i)
528 AccountData data;
529 m_accountData[i] = data;
532 QueryResult *result = CharacterDatabase.PQuery("SELECT type, time, data FROM account_data WHERE account='%u'", GetAccountId());
534 if(!result)
535 return;
539 Field *fields = result->Fetch();
541 uint32 type = fields[0].GetUInt32();
542 if(type < NUM_ACCOUNT_DATA_TYPES)
544 m_accountData[type].Time = fields[1].GetUInt32();
545 m_accountData[type].Data = fields[2].GetCppString();
547 } while (result->NextRow());
549 delete result;
552 void WorldSession::SetAccountData(uint32 type, time_t time_, std::string data)
554 m_accountData[type].Time = time_;
555 m_accountData[type].Data = data;
557 uint32 acc = GetAccountId();
559 CharacterDatabase.BeginTransaction ();
560 CharacterDatabase.PExecute("DELETE FROM account_data WHERE account='%u' AND type='%u'", acc, type);
561 CharacterDatabase.escape_string(data);
562 CharacterDatabase.PExecute("INSERT INTO account_data VALUES ('%u','%u','%u','%s')", acc, type, (uint32)time_, data.c_str());
563 CharacterDatabase.CommitTransaction ();
566 void WorldSession::ReadMovementInfo(WorldPacket &data, MovementInfo *mi)
568 CHECK_PACKET_SIZE(data, data.rpos()+4+2+4+4+4+4+4);
569 data >> mi->flags;
570 data >> mi->unk1;
571 data >> mi->time;
572 data >> mi->x;
573 data >> mi->y;
574 data >> mi->z;
575 data >> mi->o;
577 if(mi->flags & MOVEMENTFLAG_ONTRANSPORT)
579 CHECK_PACKET_SIZE(data, data.rpos()+8+4+4+4+4+4+1);
580 data >> mi->t_guid;
581 data >> mi->t_x;
582 data >> mi->t_y;
583 data >> mi->t_z;
584 data >> mi->t_o;
585 data >> mi->t_time;
586 data >> mi->t_seat;
589 if((mi->flags & (MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_FLYING2)) || (mi->unk1 & 0x20))
591 CHECK_PACKET_SIZE(data, data.rpos()+4);
592 data >> mi->s_pitch;
595 CHECK_PACKET_SIZE(data, data.rpos()+4);
596 data >> mi->fallTime;
598 if(mi->flags & MOVEMENTFLAG_JUMPING)
600 CHECK_PACKET_SIZE(data, data.rpos()+4+4+4+4);
601 data >> mi->j_unk;
602 data >> mi->j_sinAngle;
603 data >> mi->j_cosAngle;
604 data >> mi->j_xyspeed;
607 if(mi->flags & MOVEMENTFLAG_SPLINE)
609 CHECK_PACKET_SIZE(data, data.rpos()+4);
610 data >> mi->u_unk1;
614 void WorldSession::ReadAddonsInfo(WorldPacket &data)
616 if (data.rpos() + 4 > data.size())
617 return;
618 uint32 size;
619 data >> size;
621 if(!size)
622 return;
624 uLongf uSize = size;
626 uint32 pos = data.rpos();
628 ByteBuffer addonInfo;
629 addonInfo.resize(size);
631 if (uncompress(const_cast<uint8*>(addonInfo.contents()), &uSize, const_cast<uint8*>(data.contents() + pos), data.size() - pos) == Z_OK)
633 uint32 addonsCount;
634 addonInfo >> addonsCount; // addons count
636 for(uint32 i = 0; i < addonsCount; ++i)
638 std::string addonName;
639 uint8 enabled;
640 uint32 crc, unk1;
642 // check next addon data format correctness
643 if(addonInfo.rpos()+1 > addonInfo.size())
644 return;
646 addonInfo >> addonName;
648 // recheck next addon data format correctness
649 if(addonInfo.rpos()+1+4+4 > addonInfo.size())
650 return;
652 addonInfo >> enabled >> crc >> unk1;
654 sLog.outDebug("ADDON: Name: %s, Enabled: 0x%x, CRC: 0x%x, Unknown2: 0x%x", addonName.c_str(), enabled, crc, unk1);
656 m_addonsList.push_back(AddonInfo(addonName, enabled, crc));
659 uint32 unk2;
660 addonInfo >> unk2;
662 if(addonInfo.rpos() != addonInfo.size())
663 sLog.outDebug("packet under read!");
665 else
666 sLog.outError("Addon packet uncompress error!");
669 void WorldSession::SendAddonsInfo()
671 unsigned char tdata[256] =
673 0xC3, 0x5B, 0x50, 0x84, 0xB9, 0x3E, 0x32, 0x42, 0x8C, 0xD0, 0xC7, 0x48, 0xFA, 0x0E, 0x5D, 0x54,
674 0x5A, 0xA3, 0x0E, 0x14, 0xBA, 0x9E, 0x0D, 0xB9, 0x5D, 0x8B, 0xEE, 0xB6, 0x84, 0x93, 0x45, 0x75,
675 0xFF, 0x31, 0xFE, 0x2F, 0x64, 0x3F, 0x3D, 0x6D, 0x07, 0xD9, 0x44, 0x9B, 0x40, 0x85, 0x59, 0x34,
676 0x4E, 0x10, 0xE1, 0xE7, 0x43, 0x69, 0xEF, 0x7C, 0x16, 0xFC, 0xB4, 0xED, 0x1B, 0x95, 0x28, 0xA8,
677 0x23, 0x76, 0x51, 0x31, 0x57, 0x30, 0x2B, 0x79, 0x08, 0x50, 0x10, 0x1C, 0x4A, 0x1A, 0x2C, 0xC8,
678 0x8B, 0x8F, 0x05, 0x2D, 0x22, 0x3D, 0xDB, 0x5A, 0x24, 0x7A, 0x0F, 0x13, 0x50, 0x37, 0x8F, 0x5A,
679 0xCC, 0x9E, 0x04, 0x44, 0x0E, 0x87, 0x01, 0xD4, 0xA3, 0x15, 0x94, 0x16, 0x34, 0xC6, 0xC2, 0xC3,
680 0xFB, 0x49, 0xFE, 0xE1, 0xF9, 0xDA, 0x8C, 0x50, 0x3C, 0xBE, 0x2C, 0xBB, 0x57, 0xED, 0x46, 0xB9,
681 0xAD, 0x8B, 0xC6, 0xDF, 0x0E, 0xD6, 0x0F, 0xBE, 0x80, 0xB3, 0x8B, 0x1E, 0x77, 0xCF, 0xAD, 0x22,
682 0xCF, 0xB7, 0x4B, 0xCF, 0xFB, 0xF0, 0x6B, 0x11, 0x45, 0x2D, 0x7A, 0x81, 0x18, 0xF2, 0x92, 0x7E,
683 0x98, 0x56, 0x5D, 0x5E, 0x69, 0x72, 0x0A, 0x0D, 0x03, 0x0A, 0x85, 0xA2, 0x85, 0x9C, 0xCB, 0xFB,
684 0x56, 0x6E, 0x8F, 0x44, 0xBB, 0x8F, 0x02, 0x22, 0x68, 0x63, 0x97, 0xBC, 0x85, 0xBA, 0xA8, 0xF7,
685 0xB5, 0x40, 0x68, 0x3C, 0x77, 0x86, 0x6F, 0x4B, 0xD7, 0x88, 0xCA, 0x8A, 0xD7, 0xCE, 0x36, 0xF0,
686 0x45, 0x6E, 0xD5, 0x64, 0x79, 0x0F, 0x17, 0xFC, 0x64, 0xDD, 0x10, 0x6F, 0xF3, 0xF5, 0xE0, 0xA6,
687 0xC3, 0xFB, 0x1B, 0x8C, 0x29, 0xEF, 0x8E, 0xE5, 0x34, 0xCB, 0xD1, 0x2A, 0xCE, 0x79, 0xC3, 0x9A,
688 0x0D, 0x36, 0xEA, 0x01, 0xE0, 0xAA, 0x91, 0x20, 0x54, 0xF0, 0x72, 0xD8, 0x1E, 0xC7, 0x89, 0xD2
691 WorldPacket data(SMSG_ADDON_INFO, 4);
693 for(AddonsList::iterator itr = m_addonsList.begin(); itr != m_addonsList.end(); ++itr)
695 uint8 state = 2; // 2 is sent here
696 data << uint8(state);
698 uint8 unk1 = 1; // 1 is sent here
699 data << uint8(unk1);
700 if (unk1)
702 uint8 unk2 = (itr->CRC != 0x4c1c776d); // If addon is Standard addon CRC
703 data << uint8(unk2);
704 if (unk2) // if CRC is wrong, add public key (client need it)
705 data.append(tdata, sizeof(tdata));
707 data << uint32(0);
710 uint8 unk3 = 0; // 0 is sent here
711 data << uint8(unk3);
712 if (unk3)
714 // String, 256 (null terminated?)
715 data << uint8(0);
719 m_addonsList.clear();
721 uint32 count = 0;
722 data << uint32(count);
723 /*for(uint32 i = 0; i < count; ++i)
725 uint32
726 string (16 bytes)
727 string (16 bytes)
728 uint32
731 SendPacket(&data);