[8483] Implement glyph 43361.
[getmangos.git] / src / game / WorldSession.cpp
blob44d55fbcdadb9005e86913d342e0e79b12cacf0d
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 "MapManager.h"
38 #include "SocialMgr.h"
39 #include "zlib/zlib.h"
41 /// WorldSession constructor
42 WorldSession::WorldSession(uint32 id, WorldSocket *sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale) :
43 LookingForGroup_auto_join(false), LookingForGroup_auto_add(false), m_muteTime(mute_time),
44 _player(NULL), m_Socket(sock),_security(sec), _accountId(id), m_expansion(expansion),
45 m_sessionDbcLocale(sWorld.GetAvailableDbcLocale(locale)), m_sessionDbLocaleIndex(objmgr.GetIndexForLocale(locale)),
46 _logoutTime(0), m_inQueue(false), m_playerLoading(false), m_playerLogout(false), m_playerRecentlyLogout(false),
47 m_latency(0), m_TutorialsChanged(false)
49 if (sock)
51 m_Address = sock->GetRemoteAddress ();
52 sock->AddReference ();
56 /// WorldSession destructor
57 WorldSession::~WorldSession()
59 ///- unload player if not unloaded
60 if (_player)
61 LogoutPlayer (true);
63 /// - If have unclosed socket, close it
64 if (m_Socket)
66 m_Socket->CloseSocket ();
67 m_Socket->RemoveReference ();
68 m_Socket = NULL;
71 ///- empty incoming packet queue
72 WorldPacket* packet;
73 while(_recvQueue.next(packet))
74 delete packet;
77 void WorldSession::SizeError(WorldPacket const& packet, uint32 size) const
79 sLog.outError("Client (account %u) send packet %s (%u) with size " SIZEFMTD " 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 /// Logging helper for unexpected opcodes
151 void WorldSession::LogUnprocessedTail(WorldPacket *packet)
153 sLog.outError( "SESSION: opcode %s (0x%.4X) have unprocessed tail data (read stop at %u from %u)",
154 LookupOpcodeName(packet->GetOpcode()),
155 packet->GetOpcode(),
156 packet->rpos(),packet->wpos());
159 /// Update the WorldSession (triggered by World update)
160 bool WorldSession::Update(uint32 /*diff*/)
162 ///- Retrieve packets from the receive queue and call the appropriate handlers
163 /// not proccess packets if socket already closed
164 WorldPacket* packet;
165 while (_recvQueue.next(packet) && m_Socket && !m_Socket->IsClosed ())
167 /*#if 1
168 sLog.outError( "MOEP: %s (0x%.4X)",
169 LookupOpcodeName(packet->GetOpcode()),
170 packet->GetOpcode());
171 #endif*/
173 if(packet->GetOpcode() >= NUM_MSG_TYPES)
175 sLog.outError( "SESSION: received non-existed opcode %s (0x%.4X)",
176 LookupOpcodeName(packet->GetOpcode()),
177 packet->GetOpcode());
179 else
181 OpcodeHandler& opHandle = opcodeTable[packet->GetOpcode()];
184 switch (opHandle.status)
186 case STATUS_LOGGEDIN:
187 if(!_player)
189 // skip STATUS_LOGGEDIN opcode unexpected errors if player logout sometime ago - this can be network lag delayed packets
190 if(!m_playerRecentlyLogout)
191 LogUnexpectedOpcode(packet, "the player has not logged in yet");
193 else if(_player->IsInWorld())
195 (this->*opHandle.handler)(*packet);
196 if (sLog.IsOutDebug() && packet->rpos() < packet->wpos())
197 LogUnprocessedTail(packet);
199 // lag can cause STATUS_LOGGEDIN opcodes to arrive after the player started a transfer
200 break;
201 case STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT:
202 if(!_player && !m_playerRecentlyLogout)
204 LogUnexpectedOpcode(packet, "the player has not logged in yet and not recently logout");
206 else
208 // not expected _player or must checked in packet hanlder
209 (this->*opHandle.handler)(*packet);
210 if (sLog.IsOutDebug() && packet->rpos() < packet->wpos())
211 LogUnprocessedTail(packet);
213 break;
214 case STATUS_TRANSFER:
215 if(!_player)
216 LogUnexpectedOpcode(packet, "the player has not logged in yet");
217 else if(_player->IsInWorld())
218 LogUnexpectedOpcode(packet, "the player is still in world");
219 else
221 (this->*opHandle.handler)(*packet);
222 if (sLog.IsOutDebug() && packet->rpos() < packet->wpos())
223 LogUnprocessedTail(packet);
225 break;
226 case STATUS_AUTHED:
227 // prevent cheating with skip queue wait
228 if(m_inQueue)
230 LogUnexpectedOpcode(packet, "the player not pass queue yet");
231 break;
234 // single from authed time opcodes send in to after logout time
235 // and before other STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT opcodes.
236 if (packet->GetOpcode() != CMSG_SET_ACTIVE_VOICE_CHANNEL)
237 m_playerRecentlyLogout = false;
239 (this->*opHandle.handler)(*packet);
240 if (sLog.IsOutDebug() && packet->rpos() < packet->wpos())
241 LogUnprocessedTail(packet);
242 break;
243 case STATUS_NEVER:
244 sLog.outError( "SESSION: received not allowed opcode %s (0x%.4X)",
245 LookupOpcodeName(packet->GetOpcode()),
246 packet->GetOpcode());
247 break;
250 catch(ByteBufferException &)
252 sLog.outError("WorldSession::Update ByteBufferException occured while parsing a packet (opcode: %u) from client %s, accountid=%i. Skipped packet.",
253 packet->GetOpcode(), GetRemoteAddress().c_str(), GetAccountId());
254 if(sLog.IsOutDebug())
256 sLog.outDebug("Dumping error causing packet:");
257 packet->hexlike();
262 delete packet;
265 ///- Cleanup socket pointer if need
266 if (m_Socket && m_Socket->IsClosed ())
268 m_Socket->RemoveReference ();
269 m_Socket = NULL;
272 ///- If necessary, log the player out
273 time_t currTime = time(NULL);
274 if (!m_Socket || (ShouldLogOut(currTime) && !m_playerLoading))
275 LogoutPlayer(true);
277 if (!m_Socket)
278 return false; //Will remove this session from the world session map
280 return true;
283 /// %Log the player out
284 void WorldSession::LogoutPlayer(bool Save)
286 // finish pending transfers before starting the logout
287 while(_player && _player->IsBeingTeleportedFar())
288 HandleMoveWorldportAckOpcode();
290 m_playerLogout = true;
292 if (_player)
294 if (uint64 lguid = GetPlayer()->GetLootGUID())
295 DoLootRelease(lguid);
297 ///- If the player just died before logging out, make him appear as a ghost
298 //FIXME: logout must be delayed in case lost connection with client in time of combat
299 if (_player->GetDeathTimer())
301 _player->getHostilRefManager().deleteReferences();
302 _player->BuildPlayerRepop();
303 _player->RepopAtGraveyard();
305 else if (!_player->getAttackers().empty())
307 _player->CombatStop();
308 _player->getHostilRefManager().setOnlineOfflineState(false);
309 _player->RemoveAllAurasOnDeath();
311 // build set of player who attack _player or who have pet attacking of _player
312 std::set<Player*> aset;
313 for(Unit::AttackerSet::const_iterator itr = _player->getAttackers().begin(); itr != _player->getAttackers().end(); ++itr)
315 Unit* owner = (*itr)->GetOwner(); // including player controlled case
316 if(owner)
318 if(owner->GetTypeId()==TYPEID_PLAYER)
319 aset.insert((Player*)owner);
321 else
322 if((*itr)->GetTypeId()==TYPEID_PLAYER)
323 aset.insert((Player*)(*itr));
326 _player->SetPvPDeath(!aset.empty());
327 _player->KillPlayer();
328 _player->BuildPlayerRepop();
329 _player->RepopAtGraveyard();
331 // give honor to all attackers from set like group case
332 for(std::set<Player*>::const_iterator itr = aset.begin(); itr != aset.end(); ++itr)
333 (*itr)->RewardHonor(_player,aset.size());
335 // give bg rewards and update counters like kill by first from attackers
336 // this can't be called for all attackers.
337 if(!aset.empty())
338 if(BattleGround *bg = _player->GetBattleGround())
339 bg->HandleKillPlayer(_player,*aset.begin());
341 else if(_player->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION))
343 // this will kill character by SPELL_AURA_SPIRIT_OF_REDEMPTION
344 _player->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT);
345 //_player->SetDeathPvP(*); set at SPELL_AURA_SPIRIT_OF_REDEMPTION apply time
346 _player->KillPlayer();
347 _player->BuildPlayerRepop();
348 _player->RepopAtGraveyard();
350 //drop a flag if player is carrying it
351 if(BattleGround *bg = _player->GetBattleGround())
352 bg->EventPlayerLoggedOut(_player);
354 ///- Teleport to home if the player is in an invalid instance
355 if(!_player->m_InstanceValid && !_player->isGameMaster())
357 _player->TeleportTo(_player->m_homebindMapId, _player->m_homebindX, _player->m_homebindY, _player->m_homebindZ, _player->GetOrientation());
358 //this is a bad place to call for far teleport because we need player to be in world for successful logout
359 //maybe we should implement delayed far teleport logout?
362 // FG: finish pending transfers after starting the logout
363 // this should fix players beeing able to logout and login back with full hp at death position
364 while(_player->IsBeingTeleportedFar())
365 HandleMoveWorldportAckOpcode();
367 for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i)
369 if(BattleGroundQueueTypeId bgQueueTypeId = _player->GetBattleGroundQueueTypeId(i))
371 _player->RemoveBattleGroundQueueId(bgQueueTypeId);
372 sBattleGroundMgr.m_BattleGroundQueues[ bgQueueTypeId ].RemovePlayer(_player->GetGUID(), true);
376 ///- Reset the online field in the account table
377 // 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
378 //No SQL injection as AccountID is uint32
379 loginDatabase.PExecute("UPDATE account SET online = 0 WHERE id = '%u'", GetAccountId());
381 ///- If the player is in a guild, update the guild roster and broadcast a logout message to other guild members
382 Guild *guild = objmgr.GetGuildById(_player->GetGuildId());
383 if(guild)
385 guild->SetMemberStats(_player->GetGUID());
386 guild->UpdateLogoutTime(_player->GetGUID());
388 WorldPacket data(SMSG_GUILD_EVENT, (1+1+12+8)); // name limited to 12 in character table.
389 data<<(uint8)GE_SIGNED_OFF;
390 data<<(uint8)1;
391 data<<_player->GetName();
392 data<<_player->GetGUID();
393 guild->BroadcastPacket(&data);
396 ///- Remove pet
397 _player->RemovePet(NULL,PET_SAVE_AS_CURRENT, true);
399 ///- empty buyback items and save the player in the database
400 // some save parts only correctly work in case player present in map/player_lists (pets, etc)
401 if(Save)
403 uint32 eslot;
404 for(int j = BUYBACK_SLOT_START; j < BUYBACK_SLOT_END; ++j)
406 eslot = j - BUYBACK_SLOT_START;
407 _player->SetUInt64Value(PLAYER_FIELD_VENDORBUYBACK_SLOT_1 + (eslot * 2), 0);
408 _player->SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1 + eslot, 0);
409 _player->SetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1 + eslot, 0);
411 _player->SaveToDB();
414 ///- Leave all channels before player delete...
415 _player->CleanupChannels();
417 ///- If the player is in a group (or invited), remove him. If the group if then only 1 person, disband the group.
418 _player->UninviteFromGroup();
420 // remove player from the group if he is:
421 // a) in group; b) not in raid group; c) logging out normally (not being kicked or disconnected)
422 if(_player->GetGroup() && !_player->GetGroup()->isRaidGroup() && m_Socket)
423 _player->RemoveFromGroup();
425 ///- Send update to group
426 if(_player->GetGroup())
427 _player->GetGroup()->SendUpdate();
429 ///- Broadcast a logout message to the player's friends
430 sSocialMgr.SendFriendStatus(_player, FRIEND_OFFLINE, _player->GetGUIDLow(), true);
431 sSocialMgr.RemovePlayerSocial (_player->GetGUIDLow ());
433 ///- Remove the player from the world
434 // the player may not be in the world when logging out
435 // e.g if he got disconnected during a transfer to another map
436 // calls to GetMap in this case may cause crashes
437 Map* _map = _player->GetMap();
438 _map->Remove(_player, true);
439 SetPlayer(NULL); // deleted in Remove call
441 ///- Send the 'logout complete' packet to the client
442 WorldPacket data( SMSG_LOGOUT_COMPLETE, 0 );
443 SendPacket( &data );
445 ///- Since each account can only have one online character at any given time, ensure all characters for active account are marked as offline
446 //No SQL injection as AccountId is uint32
447 CharacterDatabase.PExecute("UPDATE characters SET online = 0 WHERE account = '%u'",
448 GetAccountId());
449 sLog.outDebug( "SESSION: Sent SMSG_LOGOUT_COMPLETE Message" );
452 m_playerLogout = false;
453 m_playerRecentlyLogout = true;
454 LogoutRequest(0);
457 /// Kick a player out of the World
458 void WorldSession::KickPlayer()
460 if (m_Socket)
461 m_Socket->CloseSocket ();
464 /// Cancel channeling handler
466 void WorldSession::SendAreaTriggerMessage(const char* Text, ...)
468 va_list ap;
469 char szStr [1024];
470 szStr[0] = '\0';
472 va_start(ap, Text);
473 vsnprintf( szStr, 1024, Text, ap );
474 va_end(ap);
476 uint32 length = strlen(szStr)+1;
477 WorldPacket data(SMSG_AREA_TRIGGER_MESSAGE, 4+length);
478 data << length;
479 data << szStr;
480 SendPacket(&data);
483 void WorldSession::SendNotification(const char *format,...)
485 if(format)
487 va_list ap;
488 char szStr [1024];
489 szStr[0] = '\0';
490 va_start(ap, format);
491 vsnprintf( szStr, 1024, format, ap );
492 va_end(ap);
494 WorldPacket data(SMSG_NOTIFICATION, (strlen(szStr)+1));
495 data << szStr;
496 SendPacket(&data);
500 void WorldSession::SendNotification(int32 string_id,...)
502 char const* format = GetMangosString(string_id);
503 if(format)
505 va_list ap;
506 char szStr [1024];
507 szStr[0] = '\0';
508 va_start(ap, string_id);
509 vsnprintf( szStr, 1024, format, ap );
510 va_end(ap);
512 WorldPacket data(SMSG_NOTIFICATION, (strlen(szStr)+1));
513 data << szStr;
514 SendPacket(&data);
518 void WorldSession::SendSetPhaseShift(uint32 PhaseShift)
520 WorldPacket data(SMSG_SET_PHASE_SHIFT, 4);
521 data << uint32(PhaseShift);
522 SendPacket(&data);
525 const char * WorldSession::GetMangosString( int32 entry ) const
527 return objmgr.GetMangosString(entry,GetSessionDbLocaleIndex());
530 void WorldSession::Handle_NULL( WorldPacket& recvPacket )
532 sLog.outError( "SESSION: received unhandled opcode %s (0x%.4X)",
533 LookupOpcodeName(recvPacket.GetOpcode()),
534 recvPacket.GetOpcode());
537 void WorldSession::Handle_EarlyProccess( WorldPacket& recvPacket )
539 sLog.outError( "SESSION: received opcode %s (0x%.4X) that must be processed in WorldSocket::OnRead",
540 LookupOpcodeName(recvPacket.GetOpcode()),
541 recvPacket.GetOpcode());
544 void WorldSession::Handle_ServerSide( WorldPacket& recvPacket )
546 sLog.outError( "SESSION: received server-side opcode %s (0x%.4X)",
547 LookupOpcodeName(recvPacket.GetOpcode()),
548 recvPacket.GetOpcode());
551 void WorldSession::Handle_Deprecated( WorldPacket& recvPacket )
553 sLog.outError( "SESSION: received deprecated opcode %s (0x%.4X)",
554 LookupOpcodeName(recvPacket.GetOpcode()),
555 recvPacket.GetOpcode());
558 void WorldSession::SendAuthWaitQue(uint32 position)
560 if(position == 0)
562 WorldPacket packet( SMSG_AUTH_RESPONSE, 1 );
563 packet << uint8( AUTH_OK );
564 SendPacket(&packet);
566 else
568 WorldPacket packet( SMSG_AUTH_RESPONSE, 5 );
569 packet << uint8( AUTH_WAIT_QUEUE );
570 packet << uint32 (position);
571 SendPacket(&packet);
575 void WorldSession::LoadGlobalAccountData()
577 LoadAccountData(
578 CharacterDatabase.PQuery("SELECT type, time, data FROM account_data WHERE account='%u'", GetAccountId()),
579 GLOBAL_CACHE_MASK
583 void WorldSession::LoadAccountData(QueryResult* result, uint32 mask)
585 for (uint32 i = 0; i < NUM_ACCOUNT_DATA_TYPES; ++i)
586 if (mask & (1 << i))
587 m_accountData[i] = AccountData();
589 if(!result)
590 return;
594 Field *fields = result->Fetch();
596 uint32 type = fields[0].GetUInt32();
597 if (type >= NUM_ACCOUNT_DATA_TYPES)
599 sLog.outError("Table `%s` have invalid account data type (%u), ignore.",
600 mask == GLOBAL_CACHE_MASK ? "account_data" : "character_account_data", type);
601 continue;
604 if ((mask & (1 << type))==0)
606 sLog.outError("Table `%s` have non appropriate for table account data type (%u), ignore.",
607 mask == GLOBAL_CACHE_MASK ? "account_data" : "character_account_data", type);
608 continue;
611 m_accountData[type].Time = fields[1].GetUInt32();
612 m_accountData[type].Data = fields[2].GetCppString();
614 } while (result->NextRow());
616 delete result;
619 void WorldSession::SetAccountData(AccountDataType type, time_t time_, std::string data)
621 if ((1 << type) & GLOBAL_CACHE_MASK)
623 uint32 acc = GetAccountId();
625 CharacterDatabase.BeginTransaction ();
626 CharacterDatabase.PExecute("DELETE FROM account_data WHERE account='%u' AND type='%u'", acc, type);
627 CharacterDatabase.escape_string(data);
628 CharacterDatabase.PExecute("INSERT INTO account_data VALUES ('%u','%u','%u','%s')", acc, type, (uint32)time_, data.c_str());
629 CharacterDatabase.CommitTransaction ();
631 else
633 // _player can be NULL and packet received after logout but m_GUID still store correct guid
634 if(!m_GUIDLow)
635 return;
637 CharacterDatabase.BeginTransaction ();
638 CharacterDatabase.PExecute("DELETE FROM character_account_data WHERE guid='%u' AND type='%u'", m_GUIDLow, type);
639 CharacterDatabase.escape_string(data);
640 CharacterDatabase.PExecute("INSERT INTO character_account_data VALUES ('%u','%u','%u','%s')", m_GUIDLow, type, (uint32)time_, data.c_str());
641 CharacterDatabase.CommitTransaction ();
644 m_accountData[type].Time = time_;
645 m_accountData[type].Data = data;
648 void WorldSession::SendAccountDataTimes()
650 WorldPacket data( SMSG_ACCOUNT_DATA_TIMES, 4+1+8*4 ); // changed in WotLK
651 data << uint32(time(NULL)); // unix time of something
652 data << uint8(1);
653 for(int i = 0; i < NUM_ACCOUNT_DATA_TYPES; ++i)
654 data << uint32(m_accountData[i].Time); // also unix time
655 SendPacket(&data);
658 void WorldSession::LoadTutorialsData()
660 for ( int aX = 0 ; aX < 8 ; ++aX )
661 m_Tutorials[ aX ] = 0;
663 QueryResult *result = CharacterDatabase.PQuery("SELECT tut0,tut1,tut2,tut3,tut4,tut5,tut6,tut7 FROM character_tutorial WHERE account = '%u'", GetAccountId());
665 if(result)
669 Field *fields = result->Fetch();
671 for (int iI = 0; iI < 8; ++iI)
672 m_Tutorials[iI] = fields[iI].GetUInt32();
674 while( result->NextRow() );
676 delete result;
679 m_TutorialsChanged = false;
682 void WorldSession::SendTutorialsData()
684 WorldPacket data(SMSG_TUTORIAL_FLAGS, 4*8);
685 for(uint32 i = 0; i < 8; ++i)
686 data << m_Tutorials[i];
687 SendPacket(&data);
690 void WorldSession::SaveTutorialsData()
692 if(!m_TutorialsChanged)
693 return;
695 uint32 Rows=0;
696 // it's better than rebuilding indexes multiple times
697 QueryResult *result = CharacterDatabase.PQuery("SELECT count(*) AS r FROM character_tutorial WHERE account = '%u'", GetAccountId());
698 if(result)
700 Rows = result->Fetch()[0].GetUInt32();
701 delete result;
704 if (Rows)
706 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'",
707 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());
709 else
711 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]);
714 m_TutorialsChanged = false;
717 void WorldSession::ReadMovementInfo(WorldPacket &data, MovementInfo *mi)
719 data >> mi->flags;
720 data >> mi->unk1;
721 data >> mi->time;
722 data >> mi->x;
723 data >> mi->y;
724 data >> mi->z;
725 data >> mi->o;
727 if(mi->HasMovementFlag(MOVEMENTFLAG_ONTRANSPORT))
729 if(!data.readPackGUID(mi->t_guid))
730 return;
732 data >> mi->t_x;
733 data >> mi->t_y;
734 data >> mi->t_z;
735 data >> mi->t_o;
736 data >> mi->t_time;
737 data >> mi->t_seat;
740 if((mi->HasMovementFlag(MovementFlags(MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_FLYING2))) || (mi->unk1 & 0x20))
742 data >> mi->s_pitch;
745 data >> mi->fallTime;
747 if(mi->HasMovementFlag(MOVEMENTFLAG_JUMPING))
749 data >> mi->j_unk;
750 data >> mi->j_sinAngle;
751 data >> mi->j_cosAngle;
752 data >> mi->j_xyspeed;
755 if(mi->HasMovementFlag(MOVEMENTFLAG_SPLINE))
757 data >> mi->u_unk1;
761 void WorldSession::ReadAddonsInfo(WorldPacket &data)
763 if (data.rpos() + 4 > data.size())
764 return;
765 uint32 size;
766 data >> size;
768 if(!size)
769 return;
771 if(size > 0xFFFFF)
773 sLog.outError("WorldSession::ReadAddonsInfo addon info too big, size %u", size);
774 return;
777 uLongf uSize = size;
779 uint32 pos = data.rpos();
781 ByteBuffer addonInfo;
782 addonInfo.resize(size);
784 if (uncompress(const_cast<uint8*>(addonInfo.contents()), &uSize, const_cast<uint8*>(data.contents() + pos), data.size() - pos) == Z_OK)
786 uint32 addonsCount;
787 addonInfo >> addonsCount; // addons count
789 for(uint32 i = 0; i < addonsCount; ++i)
791 std::string addonName;
792 uint8 enabled;
793 uint32 crc, unk1;
795 // check next addon data format correctness
796 if(addonInfo.rpos()+1 > addonInfo.size())
797 return;
799 addonInfo >> addonName;
801 addonInfo >> enabled >> crc >> unk1;
803 sLog.outDebug("ADDON: Name: %s, Enabled: 0x%x, CRC: 0x%x, Unknown2: 0x%x", addonName.c_str(), enabled, crc, unk1);
805 m_addonsList.push_back(AddonInfo(addonName, enabled, crc));
808 uint32 unk2;
809 addonInfo >> unk2;
811 if(addonInfo.rpos() != addonInfo.size())
812 sLog.outDebug("packet under read!");
814 else
815 sLog.outError("Addon packet uncompress error!");
818 void WorldSession::SendAddonsInfo()
820 unsigned char tdata[256] =
822 0xC3, 0x5B, 0x50, 0x84, 0xB9, 0x3E, 0x32, 0x42, 0x8C, 0xD0, 0xC7, 0x48, 0xFA, 0x0E, 0x5D, 0x54,
823 0x5A, 0xA3, 0x0E, 0x14, 0xBA, 0x9E, 0x0D, 0xB9, 0x5D, 0x8B, 0xEE, 0xB6, 0x84, 0x93, 0x45, 0x75,
824 0xFF, 0x31, 0xFE, 0x2F, 0x64, 0x3F, 0x3D, 0x6D, 0x07, 0xD9, 0x44, 0x9B, 0x40, 0x85, 0x59, 0x34,
825 0x4E, 0x10, 0xE1, 0xE7, 0x43, 0x69, 0xEF, 0x7C, 0x16, 0xFC, 0xB4, 0xED, 0x1B, 0x95, 0x28, 0xA8,
826 0x23, 0x76, 0x51, 0x31, 0x57, 0x30, 0x2B, 0x79, 0x08, 0x50, 0x10, 0x1C, 0x4A, 0x1A, 0x2C, 0xC8,
827 0x8B, 0x8F, 0x05, 0x2D, 0x22, 0x3D, 0xDB, 0x5A, 0x24, 0x7A, 0x0F, 0x13, 0x50, 0x37, 0x8F, 0x5A,
828 0xCC, 0x9E, 0x04, 0x44, 0x0E, 0x87, 0x01, 0xD4, 0xA3, 0x15, 0x94, 0x16, 0x34, 0xC6, 0xC2, 0xC3,
829 0xFB, 0x49, 0xFE, 0xE1, 0xF9, 0xDA, 0x8C, 0x50, 0x3C, 0xBE, 0x2C, 0xBB, 0x57, 0xED, 0x46, 0xB9,
830 0xAD, 0x8B, 0xC6, 0xDF, 0x0E, 0xD6, 0x0F, 0xBE, 0x80, 0xB3, 0x8B, 0x1E, 0x77, 0xCF, 0xAD, 0x22,
831 0xCF, 0xB7, 0x4B, 0xCF, 0xFB, 0xF0, 0x6B, 0x11, 0x45, 0x2D, 0x7A, 0x81, 0x18, 0xF2, 0x92, 0x7E,
832 0x98, 0x56, 0x5D, 0x5E, 0x69, 0x72, 0x0A, 0x0D, 0x03, 0x0A, 0x85, 0xA2, 0x85, 0x9C, 0xCB, 0xFB,
833 0x56, 0x6E, 0x8F, 0x44, 0xBB, 0x8F, 0x02, 0x22, 0x68, 0x63, 0x97, 0xBC, 0x85, 0xBA, 0xA8, 0xF7,
834 0xB5, 0x40, 0x68, 0x3C, 0x77, 0x86, 0x6F, 0x4B, 0xD7, 0x88, 0xCA, 0x8A, 0xD7, 0xCE, 0x36, 0xF0,
835 0x45, 0x6E, 0xD5, 0x64, 0x79, 0x0F, 0x17, 0xFC, 0x64, 0xDD, 0x10, 0x6F, 0xF3, 0xF5, 0xE0, 0xA6,
836 0xC3, 0xFB, 0x1B, 0x8C, 0x29, 0xEF, 0x8E, 0xE5, 0x34, 0xCB, 0xD1, 0x2A, 0xCE, 0x79, 0xC3, 0x9A,
837 0x0D, 0x36, 0xEA, 0x01, 0xE0, 0xAA, 0x91, 0x20, 0x54, 0xF0, 0x72, 0xD8, 0x1E, 0xC7, 0x89, 0xD2
840 WorldPacket data(SMSG_ADDON_INFO, 4);
842 for(AddonsList::iterator itr = m_addonsList.begin(); itr != m_addonsList.end(); ++itr)
844 uint8 state = 2; // 2 is sent here
845 data << uint8(state);
847 uint8 unk1 = 1; // 1 is sent here
848 data << uint8(unk1);
849 if (unk1)
851 uint8 unk2 = (itr->CRC != 0x4c1c776d); // If addon is Standard addon CRC
852 data << uint8(unk2);
853 if (unk2) // if CRC is wrong, add public key (client need it)
854 data.append(tdata, sizeof(tdata));
856 data << uint32(0);
859 uint8 unk3 = 0; // 0 is sent here
860 data << uint8(unk3);
861 if (unk3)
863 // String, 256 (null terminated?)
864 data << uint8(0);
868 m_addonsList.clear();
870 uint32 count = 0;
871 data << uint32(count);
872 /*for(uint32 i = 0; i < count; ++i)
874 uint32
875 string (16 bytes)
876 string (16 bytes)
877 uint32
880 SendPacket(&data);
883 void WorldSession::SetPlayer( Player *plr )
885 _player = plr;
887 // set m_GUID that can be used while player loggined and later until m_playerRecentlyLogout not reset
888 if(_player)
889 m_GUIDLow = _player->GetGUIDLow();