[9290] Some cleanups in realmd, no functional changes
[getmangos.git] / src / game / WorldSession.cpp
blob2bc6a842850e880211fa86ce05d5ce31a8c8fe6a
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 WorldPacket data(SMSG_GUILD_EVENT, (1+1+12+8)); // name limited to 12 in character table.
390 data<<(uint8)GE_SIGNED_OFF;
391 data<<(uint8)1;
392 data<<_player->GetName();
393 data<<_player->GetGUID();
394 guild->BroadcastPacket(&data);
397 ///- Remove pet
398 _player->RemovePet(NULL,PET_SAVE_AS_CURRENT, true);
400 ///- empty buyback items and save the player in the database
401 // some save parts only correctly work in case player present in map/player_lists (pets, etc)
402 if(Save)
404 uint32 eslot;
405 for(int j = BUYBACK_SLOT_START; j < BUYBACK_SLOT_END; ++j)
407 eslot = j - BUYBACK_SLOT_START;
408 _player->SetUInt64Value(PLAYER_FIELD_VENDORBUYBACK_SLOT_1 + (eslot * 2), 0);
409 _player->SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1 + eslot, 0);
410 _player->SetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1 + eslot, 0);
412 _player->SaveToDB();
415 ///- Leave all channels before player delete...
416 _player->CleanupChannels();
418 ///- If the player is in a group (or invited), remove him. If the group if then only 1 person, disband the group.
419 _player->UninviteFromGroup();
421 // remove player from the group if he is:
422 // a) in group; b) not in raid group; c) logging out normally (not being kicked or disconnected)
423 if(_player->GetGroup() && !_player->GetGroup()->isRaidGroup() && m_Socket)
424 _player->RemoveFromGroup();
426 ///- Send update to group
427 if(_player->GetGroup())
428 _player->GetGroup()->SendUpdate();
430 ///- Broadcast a logout message to the player's friends
431 sSocialMgr.SendFriendStatus(_player, FRIEND_OFFLINE, _player->GetGUIDLow(), true);
432 sSocialMgr.RemovePlayerSocial (_player->GetGUIDLow ());
434 ///- Remove the player from the world
435 // the player may not be in the world when logging out
436 // e.g if he got disconnected during a transfer to another map
437 // calls to GetMap in this case may cause crashes
438 Map* _map = _player->GetMap();
439 _map->Remove(_player, true);
440 SetPlayer(NULL); // deleted in Remove call
442 ///- Send the 'logout complete' packet to the client
443 WorldPacket data( SMSG_LOGOUT_COMPLETE, 0 );
444 SendPacket( &data );
446 ///- Since each account can only have one online character at any given time, ensure all characters for active account are marked as offline
447 //No SQL injection as AccountId is uint32
448 CharacterDatabase.PExecute("UPDATE characters SET online = 0 WHERE account = '%u'",
449 GetAccountId());
450 sLog.outDebug( "SESSION: Sent SMSG_LOGOUT_COMPLETE Message" );
453 m_playerLogout = false;
454 m_playerSave = false;
455 m_playerRecentlyLogout = true;
456 LogoutRequest(0);
459 /// Kick a player out of the World
460 void WorldSession::KickPlayer()
462 if (m_Socket)
463 m_Socket->CloseSocket ();
466 /// Cancel channeling handler
468 void WorldSession::SendAreaTriggerMessage(const char* Text, ...)
470 va_list ap;
471 char szStr [1024];
472 szStr[0] = '\0';
474 va_start(ap, Text);
475 vsnprintf( szStr, 1024, Text, ap );
476 va_end(ap);
478 uint32 length = strlen(szStr)+1;
479 WorldPacket data(SMSG_AREA_TRIGGER_MESSAGE, 4+length);
480 data << length;
481 data << szStr;
482 SendPacket(&data);
485 void WorldSession::SendNotification(const char *format,...)
487 if(format)
489 va_list ap;
490 char szStr [1024];
491 szStr[0] = '\0';
492 va_start(ap, format);
493 vsnprintf( szStr, 1024, format, ap );
494 va_end(ap);
496 WorldPacket data(SMSG_NOTIFICATION, (strlen(szStr)+1));
497 data << szStr;
498 SendPacket(&data);
502 void WorldSession::SendNotification(int32 string_id,...)
504 char const* format = GetMangosString(string_id);
505 if(format)
507 va_list ap;
508 char szStr [1024];
509 szStr[0] = '\0';
510 va_start(ap, string_id);
511 vsnprintf( szStr, 1024, format, ap );
512 va_end(ap);
514 WorldPacket data(SMSG_NOTIFICATION, (strlen(szStr)+1));
515 data << szStr;
516 SendPacket(&data);
520 void WorldSession::SendSetPhaseShift(uint32 PhaseShift)
522 WorldPacket data(SMSG_SET_PHASE_SHIFT, 4);
523 data << uint32(PhaseShift);
524 SendPacket(&data);
527 const char * WorldSession::GetMangosString( int32 entry ) const
529 return sObjectMgr.GetMangosString(entry,GetSessionDbLocaleIndex());
532 void WorldSession::Handle_NULL( WorldPacket& recvPacket )
534 sLog.outError( "SESSION: received unhandled opcode %s (0x%.4X)",
535 LookupOpcodeName(recvPacket.GetOpcode()),
536 recvPacket.GetOpcode());
539 void WorldSession::Handle_EarlyProccess( WorldPacket& recvPacket )
541 sLog.outError( "SESSION: received opcode %s (0x%.4X) that must be processed in WorldSocket::OnRead",
542 LookupOpcodeName(recvPacket.GetOpcode()),
543 recvPacket.GetOpcode());
546 void WorldSession::Handle_ServerSide( WorldPacket& recvPacket )
548 sLog.outError( "SESSION: received server-side opcode %s (0x%.4X)",
549 LookupOpcodeName(recvPacket.GetOpcode()),
550 recvPacket.GetOpcode());
553 void WorldSession::Handle_Deprecated( WorldPacket& recvPacket )
555 sLog.outError( "SESSION: received deprecated opcode %s (0x%.4X)",
556 LookupOpcodeName(recvPacket.GetOpcode()),
557 recvPacket.GetOpcode());
560 void WorldSession::SendAuthWaitQue(uint32 position)
562 if(position == 0)
564 WorldPacket packet( SMSG_AUTH_RESPONSE, 1 );
565 packet << uint8( AUTH_OK );
566 SendPacket(&packet);
568 else
570 WorldPacket packet( SMSG_AUTH_RESPONSE, 1+4+1 );
571 packet << uint8(AUTH_WAIT_QUEUE);
572 packet << uint32(position);
573 packet << uint8(0); // unk 3.3.0
574 SendPacket(&packet);
578 void WorldSession::LoadGlobalAccountData()
580 LoadAccountData(
581 CharacterDatabase.PQuery("SELECT type, time, data FROM account_data WHERE account='%u'", GetAccountId()),
582 GLOBAL_CACHE_MASK
586 void WorldSession::LoadAccountData(QueryResult* result, uint32 mask)
588 for (uint32 i = 0; i < NUM_ACCOUNT_DATA_TYPES; ++i)
589 if (mask & (1 << i))
590 m_accountData[i] = AccountData();
592 if(!result)
593 return;
597 Field *fields = result->Fetch();
599 uint32 type = fields[0].GetUInt32();
600 if (type >= NUM_ACCOUNT_DATA_TYPES)
602 sLog.outError("Table `%s` have invalid account data type (%u), ignore.",
603 mask == GLOBAL_CACHE_MASK ? "account_data" : "character_account_data", type);
604 continue;
607 if ((mask & (1 << type))==0)
609 sLog.outError("Table `%s` have non appropriate for table account data type (%u), ignore.",
610 mask == GLOBAL_CACHE_MASK ? "account_data" : "character_account_data", type);
611 continue;
614 m_accountData[type].Time = fields[1].GetUInt32();
615 m_accountData[type].Data = fields[2].GetCppString();
617 } while (result->NextRow());
619 delete result;
622 void WorldSession::SetAccountData(AccountDataType type, time_t time_, std::string data)
624 if ((1 << type) & GLOBAL_CACHE_MASK)
626 uint32 acc = GetAccountId();
628 CharacterDatabase.BeginTransaction ();
629 CharacterDatabase.PExecute("DELETE FROM account_data WHERE account='%u' AND type='%u'", acc, type);
630 std::string safe_data = data;
631 CharacterDatabase.escape_string(safe_data);
632 CharacterDatabase.PExecute("INSERT INTO account_data VALUES ('%u','%u','%u','%s')", acc, type, (uint32)time_, safe_data.c_str());
633 CharacterDatabase.CommitTransaction ();
635 else
637 // _player can be NULL and packet received after logout but m_GUID still store correct guid
638 if(!m_GUIDLow)
639 return;
641 CharacterDatabase.BeginTransaction ();
642 CharacterDatabase.PExecute("DELETE FROM character_account_data WHERE guid='%u' AND type='%u'", m_GUIDLow, type);
643 std::string safe_data = data;
644 CharacterDatabase.escape_string(safe_data);
645 CharacterDatabase.PExecute("INSERT INTO character_account_data VALUES ('%u','%u','%u','%s')", m_GUIDLow, type, (uint32)time_, safe_data.c_str());
646 CharacterDatabase.CommitTransaction ();
649 m_accountData[type].Time = time_;
650 m_accountData[type].Data = data;
653 void WorldSession::SendAccountDataTimes(uint32 mask)
655 WorldPacket data( SMSG_ACCOUNT_DATA_TIMES, 4+1+4+8*4 ); // changed in WotLK
656 data << uint32(time(NULL)); // unix time of something
657 data << uint8(1);
658 data << uint32(mask); // type mask
659 for(uint32 i = 0; i < NUM_ACCOUNT_DATA_TYPES; ++i)
660 if(mask & (1 << i))
661 data << uint32(GetAccountData(AccountDataType(i))->Time);// also unix time
662 SendPacket(&data);
665 void WorldSession::LoadTutorialsData()
667 for ( int aX = 0 ; aX < 8 ; ++aX )
668 m_Tutorials[ aX ] = 0;
670 QueryResult *result = CharacterDatabase.PQuery("SELECT tut0,tut1,tut2,tut3,tut4,tut5,tut6,tut7 FROM character_tutorial WHERE account = '%u'", GetAccountId());
672 if(result)
676 Field *fields = result->Fetch();
678 for (int iI = 0; iI < 8; ++iI)
679 m_Tutorials[iI] = fields[iI].GetUInt32();
681 while( result->NextRow() );
683 delete result;
686 m_TutorialsChanged = false;
689 void WorldSession::SendTutorialsData()
691 WorldPacket data(SMSG_TUTORIAL_FLAGS, 4*8);
692 for(uint32 i = 0; i < 8; ++i)
693 data << m_Tutorials[i];
694 SendPacket(&data);
697 void WorldSession::SaveTutorialsData()
699 if(!m_TutorialsChanged)
700 return;
702 uint32 Rows=0;
703 // it's better than rebuilding indexes multiple times
704 QueryResult *result = CharacterDatabase.PQuery("SELECT count(*) AS r FROM character_tutorial WHERE account = '%u'", GetAccountId());
705 if(result)
707 Rows = result->Fetch()[0].GetUInt32();
708 delete result;
711 if (Rows)
713 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'",
714 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());
716 else
718 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]);
721 m_TutorialsChanged = false;
724 void WorldSession::ReadAddonsInfo(WorldPacket &data)
726 if (data.rpos() + 4 > data.size())
727 return;
728 uint32 size;
729 data >> size;
731 if(!size)
732 return;
734 if(size > 0xFFFFF)
736 sLog.outError("WorldSession::ReadAddonsInfo addon info too big, size %u", size);
737 return;
740 uLongf uSize = size;
742 uint32 pos = data.rpos();
744 ByteBuffer addonInfo;
745 addonInfo.resize(size);
747 if (uncompress(const_cast<uint8*>(addonInfo.contents()), &uSize, const_cast<uint8*>(data.contents() + pos), data.size() - pos) == Z_OK)
749 uint32 addonsCount;
750 addonInfo >> addonsCount; // addons count
752 for(uint32 i = 0; i < addonsCount; ++i)
754 std::string addonName;
755 uint8 enabled;
756 uint32 crc, unk1;
758 // check next addon data format correctness
759 if(addonInfo.rpos()+1 > addonInfo.size())
760 return;
762 addonInfo >> addonName;
764 addonInfo >> enabled >> crc >> unk1;
766 sLog.outDebug("ADDON: Name: %s, Enabled: 0x%x, CRC: 0x%x, Unknown2: 0x%x", addonName.c_str(), enabled, crc, unk1);
768 m_addonsList.push_back(AddonInfo(addonName, enabled, crc));
771 uint32 unk2;
772 addonInfo >> unk2;
774 if(addonInfo.rpos() != addonInfo.size())
775 sLog.outDebug("packet under read!");
777 else
778 sLog.outError("Addon packet uncompress error!");
781 void WorldSession::SendAddonsInfo()
783 unsigned char tdata[256] =
785 0xC3, 0x5B, 0x50, 0x84, 0xB9, 0x3E, 0x32, 0x42, 0x8C, 0xD0, 0xC7, 0x48, 0xFA, 0x0E, 0x5D, 0x54,
786 0x5A, 0xA3, 0x0E, 0x14, 0xBA, 0x9E, 0x0D, 0xB9, 0x5D, 0x8B, 0xEE, 0xB6, 0x84, 0x93, 0x45, 0x75,
787 0xFF, 0x31, 0xFE, 0x2F, 0x64, 0x3F, 0x3D, 0x6D, 0x07, 0xD9, 0x44, 0x9B, 0x40, 0x85, 0x59, 0x34,
788 0x4E, 0x10, 0xE1, 0xE7, 0x43, 0x69, 0xEF, 0x7C, 0x16, 0xFC, 0xB4, 0xED, 0x1B, 0x95, 0x28, 0xA8,
789 0x23, 0x76, 0x51, 0x31, 0x57, 0x30, 0x2B, 0x79, 0x08, 0x50, 0x10, 0x1C, 0x4A, 0x1A, 0x2C, 0xC8,
790 0x8B, 0x8F, 0x05, 0x2D, 0x22, 0x3D, 0xDB, 0x5A, 0x24, 0x7A, 0x0F, 0x13, 0x50, 0x37, 0x8F, 0x5A,
791 0xCC, 0x9E, 0x04, 0x44, 0x0E, 0x87, 0x01, 0xD4, 0xA3, 0x15, 0x94, 0x16, 0x34, 0xC6, 0xC2, 0xC3,
792 0xFB, 0x49, 0xFE, 0xE1, 0xF9, 0xDA, 0x8C, 0x50, 0x3C, 0xBE, 0x2C, 0xBB, 0x57, 0xED, 0x46, 0xB9,
793 0xAD, 0x8B, 0xC6, 0xDF, 0x0E, 0xD6, 0x0F, 0xBE, 0x80, 0xB3, 0x8B, 0x1E, 0x77, 0xCF, 0xAD, 0x22,
794 0xCF, 0xB7, 0x4B, 0xCF, 0xFB, 0xF0, 0x6B, 0x11, 0x45, 0x2D, 0x7A, 0x81, 0x18, 0xF2, 0x92, 0x7E,
795 0x98, 0x56, 0x5D, 0x5E, 0x69, 0x72, 0x0A, 0x0D, 0x03, 0x0A, 0x85, 0xA2, 0x85, 0x9C, 0xCB, 0xFB,
796 0x56, 0x6E, 0x8F, 0x44, 0xBB, 0x8F, 0x02, 0x22, 0x68, 0x63, 0x97, 0xBC, 0x85, 0xBA, 0xA8, 0xF7,
797 0xB5, 0x40, 0x68, 0x3C, 0x77, 0x86, 0x6F, 0x4B, 0xD7, 0x88, 0xCA, 0x8A, 0xD7, 0xCE, 0x36, 0xF0,
798 0x45, 0x6E, 0xD5, 0x64, 0x79, 0x0F, 0x17, 0xFC, 0x64, 0xDD, 0x10, 0x6F, 0xF3, 0xF5, 0xE0, 0xA6,
799 0xC3, 0xFB, 0x1B, 0x8C, 0x29, 0xEF, 0x8E, 0xE5, 0x34, 0xCB, 0xD1, 0x2A, 0xCE, 0x79, 0xC3, 0x9A,
800 0x0D, 0x36, 0xEA, 0x01, 0xE0, 0xAA, 0x91, 0x20, 0x54, 0xF0, 0x72, 0xD8, 0x1E, 0xC7, 0x89, 0xD2
803 WorldPacket data(SMSG_ADDON_INFO, 4);
805 for(AddonsList::iterator itr = m_addonsList.begin(); itr != m_addonsList.end(); ++itr)
807 uint8 state = 2; // 2 is sent here
808 data << uint8(state);
810 uint8 unk1 = 1; // 1 is sent here
811 data << uint8(unk1);
812 if (unk1)
814 uint8 unk2 = (itr->CRC != 0x4c1c776d); // If addon is Standard addon CRC
815 data << uint8(unk2); // if 1, than add addon public signature
816 if (unk2) // if CRC is wrong, add public key (client need it)
817 data.append(tdata, sizeof(tdata));
819 data << uint32(0);
822 uint8 unk3 = 0; // 0 is sent here
823 data << uint8(unk3); // use <Addon>\<Addon>.url file or not
824 if (unk3)
826 // String, 256 (null terminated?)
827 data << uint8(0);
831 m_addonsList.clear();
833 uint32 count = 0;
834 data << uint32(count); // BannedAddons count
835 /*for(uint32 i = 0; i < count; ++i)
837 uint32
838 string (16 bytes)
839 string (16 bytes)
840 uint32
841 uint32
844 SendPacket(&data);
847 void WorldSession::SetPlayer( Player *plr )
849 _player = plr;
851 // set m_GUID that can be used while player loggined and later until m_playerRecentlyLogout not reset
852 if(_player)
853 m_GUIDLow = _player->GetGUIDLow();