[9362] Make specs work at least for action bars
[getmangos.git] / src / game / WorldSession.cpp
blob3d52ea7535f1a68b51840cbf797be5c378a6cd5e
1 /*
2 * Copyright (C) 2005-2010 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 "BattleGroundMgr.h"
36 #include "MapManager.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(sObjectMgr.GetIndexForLocale(locale)),
45 _logoutTime(0), m_inQueue(false), m_playerLoading(false), m_playerLogout(false), m_playerRecentlyLogout(false), m_playerSave(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 WorldPacket* packet;
72 while(_recvQueue.next(packet))
73 delete packet;
76 void WorldSession::SizeError(WorldPacket const& packet, uint32 size) const
78 sLog.outError("Client (account %u) send packet %s (%u) with size " SIZEFMTD " but expected %u (attempt crash server?), skipped",
79 GetAccountId(),LookupOpcodeName(packet.GetOpcode()),packet.GetOpcode(),packet.size(),size);
82 /// Get the player name
83 char const* WorldSession::GetPlayerName() const
85 return GetPlayer() ? GetPlayer()->GetName() : "<none>";
88 /// Send a packet to the client
89 void WorldSession::SendPacket(WorldPacket const* packet)
91 if (!m_Socket)
92 return;
94 #ifdef MANGOS_DEBUG
96 // Code for network use statistic
97 static uint64 sendPacketCount = 0;
98 static uint64 sendPacketBytes = 0;
100 static time_t firstTime = time(NULL);
101 static time_t lastTime = firstTime; // next 60 secs start time
103 static uint64 sendLastPacketCount = 0;
104 static uint64 sendLastPacketBytes = 0;
106 time_t cur_time = time(NULL);
108 if((cur_time - lastTime) < 60)
110 sendPacketCount+=1;
111 sendPacketBytes+=packet->size();
113 sendLastPacketCount+=1;
114 sendLastPacketBytes+=packet->size();
116 else
118 uint64 minTime = uint64(cur_time - lastTime);
119 uint64 fullTime = uint64(lastTime - firstTime);
120 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));
121 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);
123 lastTime = cur_time;
124 sendLastPacketCount = 1;
125 sendLastPacketBytes = packet->wpos(); // wpos is real written size
128 #endif // !MANGOS_DEBUG
130 if (m_Socket->SendPacket (*packet) == -1)
131 m_Socket->CloseSocket ();
134 /// Add an incoming packet to the queue
135 void WorldSession::QueuePacket(WorldPacket* new_packet)
137 _recvQueue.add(new_packet);
140 /// Logging helper for unexpected opcodes
141 void WorldSession::LogUnexpectedOpcode(WorldPacket* packet, const char *reason)
143 sLog.outError( "SESSION: received unexpected opcode %s (0x%.4X) %s",
144 LookupOpcodeName(packet->GetOpcode()),
145 packet->GetOpcode(),
146 reason);
149 /// Logging helper for unexpected opcodes
150 void WorldSession::LogUnprocessedTail(WorldPacket *packet)
152 sLog.outError( "SESSION: opcode %s (0x%.4X) have unprocessed tail data (read stop at " SIZEFMTD " from " SIZEFMTD ")",
153 LookupOpcodeName(packet->GetOpcode()),
154 packet->GetOpcode(),
155 packet->rpos(),packet->wpos());
158 /// Update the WorldSession (triggered by World update)
159 bool WorldSession::Update(uint32 /*diff*/)
161 ///- Retrieve packets from the receive queue and call the appropriate handlers
162 /// not proccess packets if socket already closed
163 WorldPacket* packet;
164 while (_recvQueue.next(packet) && m_Socket && !m_Socket->IsClosed ())
166 /*#if 1
167 sLog.outError( "MOEP: %s (0x%.4X)",
168 LookupOpcodeName(packet->GetOpcode()),
169 packet->GetOpcode());
170 #endif*/
172 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())
186 (this->*opHandle.handler)(*packet);
187 if (sLog.IsOutDebug() && packet->rpos() < packet->wpos())
188 LogUnprocessedTail(packet);
190 // lag can cause STATUS_LOGGEDIN opcodes to arrive after the player started a transfer
191 break;
192 case STATUS_LOGGEDIN_OR_RECENTLY_LOGGEDOUT:
193 if(!_player && !m_playerRecentlyLogout)
195 LogUnexpectedOpcode(packet, "the player has not logged in yet and not recently logout");
197 else
199 // not expected _player or must checked in packet hanlder
200 (this->*opHandle.handler)(*packet);
201 if (sLog.IsOutDebug() && packet->rpos() < packet->wpos())
202 LogUnprocessedTail(packet);
204 break;
205 case STATUS_TRANSFER:
206 if(!_player)
207 LogUnexpectedOpcode(packet, "the player has not logged in yet");
208 else if(_player->IsInWorld())
209 LogUnexpectedOpcode(packet, "the player is still in world");
210 else
212 (this->*opHandle.handler)(*packet);
213 if (sLog.IsOutDebug() && packet->rpos() < packet->wpos())
214 LogUnprocessedTail(packet);
216 break;
217 case STATUS_AUTHED:
218 // prevent cheating with skip queue wait
219 if(m_inQueue)
221 LogUnexpectedOpcode(packet, "the player not pass queue yet");
222 break;
225 // single from authed time opcodes send in to after logout time
226 // and before other STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT opcodes.
227 if (packet->GetOpcode() != CMSG_SET_ACTIVE_VOICE_CHANNEL)
228 m_playerRecentlyLogout = false;
230 (this->*opHandle.handler)(*packet);
231 if (sLog.IsOutDebug() && packet->rpos() < packet->wpos())
232 LogUnprocessedTail(packet);
233 break;
234 case STATUS_NEVER:
235 sLog.outError( "SESSION: received not allowed opcode %s (0x%.4X)",
236 LookupOpcodeName(packet->GetOpcode()),
237 packet->GetOpcode());
238 break;
239 case STATUS_UNHANDLED:
240 sLog.outDebug("SESSION: received not handled opcode %s (0x%.4X)",
241 LookupOpcodeName(packet->GetOpcode()),
242 packet->GetOpcode());
243 break;
244 default:
245 sLog.outError("SESSION: received wrong-status-req opcode %s (0x%.4X)",
246 LookupOpcodeName(packet->GetOpcode()),
247 packet->GetOpcode());
248 break;
251 catch(ByteBufferException &)
253 sLog.outError("WorldSession::Update ByteBufferException occured while parsing a packet (opcode: %u) from client %s, accountid=%i. Skipped packet.",
254 packet->GetOpcode(), GetRemoteAddress().c_str(), GetAccountId());
255 if(sLog.IsOutDebug())
257 sLog.outDebug("Dumping error causing packet:");
258 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;
291 m_playerSave = Save;
293 if (_player)
295 if (uint64 lguid = GetPlayer()->GetLootGUID())
296 DoLootRelease(lguid);
298 ///- If the player just died before logging out, make him appear as a ghost
299 //FIXME: logout must be delayed in case lost connection with client in time of combat
300 if (_player->GetDeathTimer())
302 _player->getHostileRefManager().deleteReferences();
303 _player->BuildPlayerRepop();
304 _player->RepopAtGraveyard();
306 else if (!_player->getAttackers().empty())
308 _player->CombatStop();
309 _player->getHostileRefManager().setOnlineOfflineState(false);
310 _player->RemoveAllAurasOnDeath();
312 // build set of player who attack _player or who have pet attacking of _player
313 std::set<Player*> aset;
314 for(Unit::AttackerSet::const_iterator itr = _player->getAttackers().begin(); itr != _player->getAttackers().end(); ++itr)
316 Unit* owner = (*itr)->GetOwner(); // including player controlled case
317 if(owner)
319 if(owner->GetTypeId()==TYPEID_PLAYER)
320 aset.insert((Player*)owner);
322 else
323 if((*itr)->GetTypeId()==TYPEID_PLAYER)
324 aset.insert((Player*)(*itr));
327 _player->SetPvPDeath(!aset.empty());
328 _player->KillPlayer();
329 _player->BuildPlayerRepop();
330 _player->RepopAtGraveyard();
332 // give honor to all attackers from set like group case
333 for(std::set<Player*>::const_iterator itr = aset.begin(); itr != aset.end(); ++itr)
334 (*itr)->RewardHonor(_player,aset.size());
336 // give bg rewards and update counters like kill by first from attackers
337 // this can't be called for all attackers.
338 if(!aset.empty())
339 if(BattleGround *bg = _player->GetBattleGround())
340 bg->HandleKillPlayer(_player,*aset.begin());
342 else if(_player->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION))
344 // this will kill character by SPELL_AURA_SPIRIT_OF_REDEMPTION
345 _player->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT);
346 //_player->SetDeathPvP(*); set at SPELL_AURA_SPIRIT_OF_REDEMPTION apply time
347 _player->KillPlayer();
348 _player->BuildPlayerRepop();
349 _player->RepopAtGraveyard();
351 //drop a flag if player is carrying it
352 if(BattleGround *bg = _player->GetBattleGround())
353 bg->EventPlayerLoggedOut(_player);
355 ///- Teleport to home if the player is in an invalid instance
356 if(!_player->m_InstanceValid && !_player->isGameMaster())
358 _player->TeleportToHomebind();
359 //this is a bad place to call for far teleport because we need player to be in world for successful logout
360 //maybe we should implement delayed far teleport logout?
363 // FG: finish pending transfers after starting the logout
364 // this should fix players beeing able to logout and login back with full hp at death position
365 while(_player->IsBeingTeleportedFar())
366 HandleMoveWorldportAckOpcode();
368 for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i)
370 if(BattleGroundQueueTypeId bgQueueTypeId = _player->GetBattleGroundQueueTypeId(i))
372 _player->RemoveBattleGroundQueueId(bgQueueTypeId);
373 sBattleGroundMgr.m_BattleGroundQueues[ bgQueueTypeId ].RemovePlayer(_player->GetGUID(), true);
377 ///- Reset the online field in the account table
378 // 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
379 // No SQL injection as AccountID is uint32
380 loginDatabase.PExecute("UPDATE account SET active_realm_id = 0 WHERE id = '%u'", GetAccountId());
382 ///- If the player is in a guild, update the guild roster and broadcast a logout message to other guild members
383 Guild *guild = sObjectMgr.GetGuildById(_player->GetGuildId());
384 if(guild)
386 guild->SetMemberStats(_player->GetGUID());
387 guild->UpdateLogoutTime(_player->GetGUID());
389 guild->BroadcastEvent(GE_SIGNED_OFF, _player->GetGUID(), 1, _player->GetName(), "", "");
392 ///- Remove pet
393 _player->RemovePet(NULL, PET_SAVE_AS_CURRENT, true);
395 ///- empty buyback items and save the player in the database
396 // some save parts only correctly work in case player present in map/player_lists (pets, etc)
397 if(Save)
399 uint32 eslot;
400 for(int j = BUYBACK_SLOT_START; j < BUYBACK_SLOT_END; ++j)
402 eslot = j - BUYBACK_SLOT_START;
403 _player->SetUInt64Value(PLAYER_FIELD_VENDORBUYBACK_SLOT_1 + (eslot * 2), 0);
404 _player->SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1 + eslot, 0);
405 _player->SetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1 + eslot, 0);
407 _player->SaveToDB();
410 ///- Leave all channels before player delete...
411 _player->CleanupChannels();
413 ///- If the player is in a group (or invited), remove him. If the group if then only 1 person, disband the group.
414 _player->UninviteFromGroup();
416 // remove player from the group if he is:
417 // a) in group; b) not in raid group; c) logging out normally (not being kicked or disconnected)
418 if(_player->GetGroup() && !_player->GetGroup()->isRaidGroup() && m_Socket)
419 _player->RemoveFromGroup();
421 ///- Send update to group
422 if(_player->GetGroup())
423 _player->GetGroup()->SendUpdate();
425 ///- Broadcast a logout message to the player's friends
426 sSocialMgr.SendFriendStatus(_player, FRIEND_OFFLINE, _player->GetGUIDLow(), true);
427 sSocialMgr.RemovePlayerSocial (_player->GetGUIDLow ());
429 ///- Remove the player from the world
430 // the player may not be in the world when logging out
431 // e.g if he got disconnected during a transfer to another map
432 // calls to GetMap in this case may cause crashes
433 Map* _map = _player->GetMap();
434 _map->Remove(_player, true);
435 SetPlayer(NULL); // deleted in Remove call
437 ///- Send the 'logout complete' packet to the client
438 WorldPacket data( SMSG_LOGOUT_COMPLETE, 0 );
439 SendPacket( &data );
441 ///- Since each account can only have one online character at any given time, ensure all characters for active account are marked as offline
442 //No SQL injection as AccountId is uint32
443 CharacterDatabase.PExecute("UPDATE characters SET online = 0 WHERE account = '%u'",
444 GetAccountId());
445 sLog.outDebug( "SESSION: Sent SMSG_LOGOUT_COMPLETE Message" );
448 m_playerLogout = false;
449 m_playerSave = false;
450 m_playerRecentlyLogout = true;
451 LogoutRequest(0);
454 /// Kick a player out of the World
455 void WorldSession::KickPlayer()
457 if (m_Socket)
458 m_Socket->CloseSocket ();
461 /// Cancel channeling handler
463 void WorldSession::SendAreaTriggerMessage(const char* Text, ...)
465 va_list ap;
466 char szStr [1024];
467 szStr[0] = '\0';
469 va_start(ap, Text);
470 vsnprintf( szStr, 1024, Text, ap );
471 va_end(ap);
473 uint32 length = strlen(szStr)+1;
474 WorldPacket data(SMSG_AREA_TRIGGER_MESSAGE, 4+length);
475 data << length;
476 data << szStr;
477 SendPacket(&data);
480 void WorldSession::SendNotification(const char *format,...)
482 if(format)
484 va_list ap;
485 char szStr [1024];
486 szStr[0] = '\0';
487 va_start(ap, format);
488 vsnprintf( szStr, 1024, format, ap );
489 va_end(ap);
491 WorldPacket data(SMSG_NOTIFICATION, (strlen(szStr)+1));
492 data << szStr;
493 SendPacket(&data);
497 void WorldSession::SendNotification(int32 string_id,...)
499 char const* format = GetMangosString(string_id);
500 if(format)
502 va_list ap;
503 char szStr [1024];
504 szStr[0] = '\0';
505 va_start(ap, string_id);
506 vsnprintf( szStr, 1024, format, ap );
507 va_end(ap);
509 WorldPacket data(SMSG_NOTIFICATION, (strlen(szStr)+1));
510 data << szStr;
511 SendPacket(&data);
515 void WorldSession::SendSetPhaseShift(uint32 PhaseShift)
517 WorldPacket data(SMSG_SET_PHASE_SHIFT, 4);
518 data << uint32(PhaseShift);
519 SendPacket(&data);
522 const char * WorldSession::GetMangosString( int32 entry ) const
524 return sObjectMgr.GetMangosString(entry,GetSessionDbLocaleIndex());
527 void WorldSession::Handle_NULL( WorldPacket& recvPacket )
529 sLog.outError( "SESSION: received unhandled opcode %s (0x%.4X)",
530 LookupOpcodeName(recvPacket.GetOpcode()),
531 recvPacket.GetOpcode());
534 void WorldSession::Handle_EarlyProccess( WorldPacket& recvPacket )
536 sLog.outError( "SESSION: received opcode %s (0x%.4X) that must be processed in WorldSocket::OnRead",
537 LookupOpcodeName(recvPacket.GetOpcode()),
538 recvPacket.GetOpcode());
541 void WorldSession::Handle_ServerSide( WorldPacket& recvPacket )
543 sLog.outError( "SESSION: received server-side opcode %s (0x%.4X)",
544 LookupOpcodeName(recvPacket.GetOpcode()),
545 recvPacket.GetOpcode());
548 void WorldSession::Handle_Deprecated( WorldPacket& recvPacket )
550 sLog.outError( "SESSION: received deprecated opcode %s (0x%.4X)",
551 LookupOpcodeName(recvPacket.GetOpcode()),
552 recvPacket.GetOpcode());
555 void WorldSession::SendAuthWaitQue(uint32 position)
557 if(position == 0)
559 WorldPacket packet( SMSG_AUTH_RESPONSE, 1 );
560 packet << uint8( AUTH_OK );
561 SendPacket(&packet);
563 else
565 WorldPacket packet( SMSG_AUTH_RESPONSE, 1+4+1 );
566 packet << uint8(AUTH_WAIT_QUEUE);
567 packet << uint32(position);
568 packet << uint8(0); // unk 3.3.0
569 SendPacket(&packet);
573 void WorldSession::LoadGlobalAccountData()
575 LoadAccountData(
576 CharacterDatabase.PQuery("SELECT type, time, data FROM account_data WHERE account='%u'", GetAccountId()),
577 GLOBAL_CACHE_MASK
581 void WorldSession::LoadAccountData(QueryResult* result, uint32 mask)
583 for (uint32 i = 0; i < NUM_ACCOUNT_DATA_TYPES; ++i)
584 if (mask & (1 << i))
585 m_accountData[i] = AccountData();
587 if(!result)
588 return;
592 Field *fields = result->Fetch();
594 uint32 type = fields[0].GetUInt32();
595 if (type >= NUM_ACCOUNT_DATA_TYPES)
597 sLog.outError("Table `%s` have invalid account data type (%u), ignore.",
598 mask == GLOBAL_CACHE_MASK ? "account_data" : "character_account_data", type);
599 continue;
602 if ((mask & (1 << type))==0)
604 sLog.outError("Table `%s` have non appropriate for table account data type (%u), ignore.",
605 mask == GLOBAL_CACHE_MASK ? "account_data" : "character_account_data", type);
606 continue;
609 m_accountData[type].Time = fields[1].GetUInt32();
610 m_accountData[type].Data = fields[2].GetCppString();
612 } while (result->NextRow());
614 delete result;
617 void WorldSession::SetAccountData(AccountDataType type, time_t time_, std::string data)
619 if ((1 << type) & GLOBAL_CACHE_MASK)
621 uint32 acc = GetAccountId();
623 CharacterDatabase.BeginTransaction ();
624 CharacterDatabase.PExecute("DELETE FROM account_data WHERE account='%u' AND type='%u'", acc, type);
625 std::string safe_data = data;
626 CharacterDatabase.escape_string(safe_data);
627 CharacterDatabase.PExecute("INSERT INTO account_data VALUES ('%u','%u','%u','%s')", acc, type, (uint32)time_, safe_data.c_str());
628 CharacterDatabase.CommitTransaction ();
630 else
632 // _player can be NULL and packet received after logout but m_GUID still store correct guid
633 if(!m_GUIDLow)
634 return;
636 CharacterDatabase.BeginTransaction ();
637 CharacterDatabase.PExecute("DELETE FROM character_account_data WHERE guid='%u' AND type='%u'", m_GUIDLow, type);
638 std::string safe_data = data;
639 CharacterDatabase.escape_string(safe_data);
640 CharacterDatabase.PExecute("INSERT INTO character_account_data VALUES ('%u','%u','%u','%s')", m_GUIDLow, type, (uint32)time_, safe_data.c_str());
641 CharacterDatabase.CommitTransaction ();
644 m_accountData[type].Time = time_;
645 m_accountData[type].Data = data;
648 void WorldSession::SendAccountDataTimes(uint32 mask)
650 WorldPacket data( SMSG_ACCOUNT_DATA_TIMES, 4+1+4+8*4 ); // changed in WotLK
651 data << uint32(time(NULL)); // unix time of something
652 data << uint8(1);
653 data << uint32(mask); // type mask
654 for(uint32 i = 0; i < NUM_ACCOUNT_DATA_TYPES; ++i)
655 if(mask & (1 << i))
656 data << uint32(GetAccountData(AccountDataType(i))->Time);// also unix time
657 SendPacket(&data);
660 void WorldSession::LoadTutorialsData()
662 for ( int aX = 0 ; aX < 8 ; ++aX )
663 m_Tutorials[ aX ] = 0;
665 QueryResult *result = CharacterDatabase.PQuery("SELECT tut0,tut1,tut2,tut3,tut4,tut5,tut6,tut7 FROM character_tutorial WHERE account = '%u'", GetAccountId());
667 if(result)
671 Field *fields = result->Fetch();
673 for (int iI = 0; iI < 8; ++iI)
674 m_Tutorials[iI] = fields[iI].GetUInt32();
676 while( result->NextRow() );
678 delete result;
681 m_TutorialsChanged = false;
684 void WorldSession::SendTutorialsData()
686 WorldPacket data(SMSG_TUTORIAL_FLAGS, 4*8);
687 for(uint32 i = 0; i < 8; ++i)
688 data << m_Tutorials[i];
689 SendPacket(&data);
692 void WorldSession::SaveTutorialsData()
694 if(!m_TutorialsChanged)
695 return;
697 uint32 Rows=0;
698 // it's better than rebuilding indexes multiple times
699 QueryResult *result = CharacterDatabase.PQuery("SELECT count(*) AS r FROM character_tutorial WHERE account = '%u'", GetAccountId());
700 if(result)
702 Rows = result->Fetch()[0].GetUInt32();
703 delete result;
706 if (Rows)
708 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'",
709 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());
711 else
713 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]);
716 m_TutorialsChanged = false;
719 void WorldSession::ReadAddonsInfo(WorldPacket &data)
721 if (data.rpos() + 4 > data.size())
722 return;
723 uint32 size;
724 data >> size;
726 if(!size)
727 return;
729 if(size > 0xFFFFF)
731 sLog.outError("WorldSession::ReadAddonsInfo addon info too big, size %u", size);
732 return;
735 uLongf uSize = size;
737 uint32 pos = data.rpos();
739 ByteBuffer addonInfo;
740 addonInfo.resize(size);
742 if (uncompress(const_cast<uint8*>(addonInfo.contents()), &uSize, const_cast<uint8*>(data.contents() + pos), data.size() - pos) == Z_OK)
744 uint32 addonsCount;
745 addonInfo >> addonsCount; // addons count
747 for(uint32 i = 0; i < addonsCount; ++i)
749 std::string addonName;
750 uint8 enabled;
751 uint32 crc, unk1;
753 // check next addon data format correctness
754 if(addonInfo.rpos()+1 > addonInfo.size())
755 return;
757 addonInfo >> addonName;
759 addonInfo >> enabled >> crc >> unk1;
761 sLog.outDebug("ADDON: Name: %s, Enabled: 0x%x, CRC: 0x%x, Unknown2: 0x%x", addonName.c_str(), enabled, crc, unk1);
763 m_addonsList.push_back(AddonInfo(addonName, enabled, crc));
766 uint32 unk2;
767 addonInfo >> unk2;
769 if(addonInfo.rpos() != addonInfo.size())
770 sLog.outDebug("packet under read!");
772 else
773 sLog.outError("Addon packet uncompress error!");
776 void WorldSession::SendAddonsInfo()
778 unsigned char tdata[256] =
780 0xC3, 0x5B, 0x50, 0x84, 0xB9, 0x3E, 0x32, 0x42, 0x8C, 0xD0, 0xC7, 0x48, 0xFA, 0x0E, 0x5D, 0x54,
781 0x5A, 0xA3, 0x0E, 0x14, 0xBA, 0x9E, 0x0D, 0xB9, 0x5D, 0x8B, 0xEE, 0xB6, 0x84, 0x93, 0x45, 0x75,
782 0xFF, 0x31, 0xFE, 0x2F, 0x64, 0x3F, 0x3D, 0x6D, 0x07, 0xD9, 0x44, 0x9B, 0x40, 0x85, 0x59, 0x34,
783 0x4E, 0x10, 0xE1, 0xE7, 0x43, 0x69, 0xEF, 0x7C, 0x16, 0xFC, 0xB4, 0xED, 0x1B, 0x95, 0x28, 0xA8,
784 0x23, 0x76, 0x51, 0x31, 0x57, 0x30, 0x2B, 0x79, 0x08, 0x50, 0x10, 0x1C, 0x4A, 0x1A, 0x2C, 0xC8,
785 0x8B, 0x8F, 0x05, 0x2D, 0x22, 0x3D, 0xDB, 0x5A, 0x24, 0x7A, 0x0F, 0x13, 0x50, 0x37, 0x8F, 0x5A,
786 0xCC, 0x9E, 0x04, 0x44, 0x0E, 0x87, 0x01, 0xD4, 0xA3, 0x15, 0x94, 0x16, 0x34, 0xC6, 0xC2, 0xC3,
787 0xFB, 0x49, 0xFE, 0xE1, 0xF9, 0xDA, 0x8C, 0x50, 0x3C, 0xBE, 0x2C, 0xBB, 0x57, 0xED, 0x46, 0xB9,
788 0xAD, 0x8B, 0xC6, 0xDF, 0x0E, 0xD6, 0x0F, 0xBE, 0x80, 0xB3, 0x8B, 0x1E, 0x77, 0xCF, 0xAD, 0x22,
789 0xCF, 0xB7, 0x4B, 0xCF, 0xFB, 0xF0, 0x6B, 0x11, 0x45, 0x2D, 0x7A, 0x81, 0x18, 0xF2, 0x92, 0x7E,
790 0x98, 0x56, 0x5D, 0x5E, 0x69, 0x72, 0x0A, 0x0D, 0x03, 0x0A, 0x85, 0xA2, 0x85, 0x9C, 0xCB, 0xFB,
791 0x56, 0x6E, 0x8F, 0x44, 0xBB, 0x8F, 0x02, 0x22, 0x68, 0x63, 0x97, 0xBC, 0x85, 0xBA, 0xA8, 0xF7,
792 0xB5, 0x40, 0x68, 0x3C, 0x77, 0x86, 0x6F, 0x4B, 0xD7, 0x88, 0xCA, 0x8A, 0xD7, 0xCE, 0x36, 0xF0,
793 0x45, 0x6E, 0xD5, 0x64, 0x79, 0x0F, 0x17, 0xFC, 0x64, 0xDD, 0x10, 0x6F, 0xF3, 0xF5, 0xE0, 0xA6,
794 0xC3, 0xFB, 0x1B, 0x8C, 0x29, 0xEF, 0x8E, 0xE5, 0x34, 0xCB, 0xD1, 0x2A, 0xCE, 0x79, 0xC3, 0x9A,
795 0x0D, 0x36, 0xEA, 0x01, 0xE0, 0xAA, 0x91, 0x20, 0x54, 0xF0, 0x72, 0xD8, 0x1E, 0xC7, 0x89, 0xD2
798 WorldPacket data(SMSG_ADDON_INFO, 4);
800 for(AddonsList::iterator itr = m_addonsList.begin(); itr != m_addonsList.end(); ++itr)
802 uint8 state = 2; // 2 is sent here
803 data << uint8(state);
805 uint8 unk1 = 1; // 1 is sent here
806 data << uint8(unk1);
807 if (unk1)
809 uint8 unk2 = (itr->CRC != 0x4c1c776d); // If addon is Standard addon CRC
810 data << uint8(unk2); // if 1, than add addon public signature
811 if (unk2) // if CRC is wrong, add public key (client need it)
812 data.append(tdata, sizeof(tdata));
814 data << uint32(0);
817 uint8 unk3 = 0; // 0 is sent here
818 data << uint8(unk3); // use <Addon>\<Addon>.url file or not
819 if (unk3)
821 // String, 256 (null terminated?)
822 data << uint8(0);
826 m_addonsList.clear();
828 uint32 count = 0;
829 data << uint32(count); // BannedAddons count
830 /*for(uint32 i = 0; i < count; ++i)
832 uint32
833 string (16 bytes)
834 string (16 bytes)
835 uint32
836 uint32
839 SendPacket(&data);
842 void WorldSession::SetPlayer( Player *plr )
844 _player = plr;
846 // set m_GUID that can be used while player loggined and later until m_playerRecentlyLogout not reset
847 if(_player)
848 m_GUIDLow = _player->GetGUIDLow();