[9033] Fixed percent mana regneration from spell 53228 and ranks buff.
[getmangos.git] / src / game / WorldSession.cpp
bloba9d0e1cb2b8f58d3fb4ab8a3ac4730b8a8c965f6
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 "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 %u from %u)",
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, 5 );
571 packet << uint8( AUTH_WAIT_QUEUE );
572 packet << uint32 (position);
573 SendPacket(&packet);
577 void WorldSession::LoadGlobalAccountData()
579 LoadAccountData(
580 CharacterDatabase.PQuery("SELECT type, time, data FROM account_data WHERE account='%u'", GetAccountId()),
581 GLOBAL_CACHE_MASK
585 void WorldSession::LoadAccountData(QueryResult* result, uint32 mask)
587 for (uint32 i = 0; i < NUM_ACCOUNT_DATA_TYPES; ++i)
588 if (mask & (1 << i))
589 m_accountData[i] = AccountData();
591 if(!result)
592 return;
596 Field *fields = result->Fetch();
598 uint32 type = fields[0].GetUInt32();
599 if (type >= NUM_ACCOUNT_DATA_TYPES)
601 sLog.outError("Table `%s` have invalid account data type (%u), ignore.",
602 mask == GLOBAL_CACHE_MASK ? "account_data" : "character_account_data", type);
603 continue;
606 if ((mask & (1 << type))==0)
608 sLog.outError("Table `%s` have non appropriate for table account data type (%u), ignore.",
609 mask == GLOBAL_CACHE_MASK ? "account_data" : "character_account_data", type);
610 continue;
613 m_accountData[type].Time = fields[1].GetUInt32();
614 m_accountData[type].Data = fields[2].GetCppString();
616 } while (result->NextRow());
618 delete result;
621 void WorldSession::SetAccountData(AccountDataType type, time_t time_, std::string data)
623 if ((1 << type) & GLOBAL_CACHE_MASK)
625 uint32 acc = GetAccountId();
627 CharacterDatabase.BeginTransaction ();
628 CharacterDatabase.PExecute("DELETE FROM account_data WHERE account='%u' AND type='%u'", acc, type);
629 CharacterDatabase.escape_string(data);
630 CharacterDatabase.PExecute("INSERT INTO account_data VALUES ('%u','%u','%u','%s')", acc, type, (uint32)time_, data.c_str());
631 CharacterDatabase.CommitTransaction ();
633 else
635 // _player can be NULL and packet received after logout but m_GUID still store correct guid
636 if(!m_GUIDLow)
637 return;
639 CharacterDatabase.BeginTransaction ();
640 CharacterDatabase.PExecute("DELETE FROM character_account_data WHERE guid='%u' AND type='%u'", m_GUIDLow, type);
641 CharacterDatabase.escape_string(data);
642 CharacterDatabase.PExecute("INSERT INTO character_account_data VALUES ('%u','%u','%u','%s')", m_GUIDLow, type, (uint32)time_, data.c_str());
643 CharacterDatabase.CommitTransaction ();
646 m_accountData[type].Time = time_;
647 m_accountData[type].Data = data;
650 void WorldSession::SendAccountDataTimes(uint32 mask)
652 WorldPacket data( SMSG_ACCOUNT_DATA_TIMES, 4+1+4+8*4 ); // changed in WotLK
653 data << uint32(time(NULL)); // unix time of something
654 data << uint8(1);
655 data << uint32(mask); // type mask
656 for(uint32 i = 0; i < NUM_ACCOUNT_DATA_TYPES; ++i)
657 if(mask & (1 << i))
658 data << uint32(GetAccountData(AccountDataType(i))->Time);// also unix time
659 SendPacket(&data);
662 void WorldSession::LoadTutorialsData()
664 for ( int aX = 0 ; aX < 8 ; ++aX )
665 m_Tutorials[ aX ] = 0;
667 QueryResult *result = CharacterDatabase.PQuery("SELECT tut0,tut1,tut2,tut3,tut4,tut5,tut6,tut7 FROM character_tutorial WHERE account = '%u'", GetAccountId());
669 if(result)
673 Field *fields = result->Fetch();
675 for (int iI = 0; iI < 8; ++iI)
676 m_Tutorials[iI] = fields[iI].GetUInt32();
678 while( result->NextRow() );
680 delete result;
683 m_TutorialsChanged = false;
686 void WorldSession::SendTutorialsData()
688 WorldPacket data(SMSG_TUTORIAL_FLAGS, 4*8);
689 for(uint32 i = 0; i < 8; ++i)
690 data << m_Tutorials[i];
691 SendPacket(&data);
694 void WorldSession::SaveTutorialsData()
696 if(!m_TutorialsChanged)
697 return;
699 uint32 Rows=0;
700 // it's better than rebuilding indexes multiple times
701 QueryResult *result = CharacterDatabase.PQuery("SELECT count(*) AS r FROM character_tutorial WHERE account = '%u'", GetAccountId());
702 if(result)
704 Rows = result->Fetch()[0].GetUInt32();
705 delete result;
708 if (Rows)
710 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'",
711 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());
713 else
715 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]);
718 m_TutorialsChanged = false;
721 void WorldSession::ReadMovementInfo(WorldPacket &data, MovementInfo *mi)
723 data >> mi->flags;
724 data >> mi->unk1;
725 data >> mi->time;
726 data >> mi->x;
727 data >> mi->y;
728 data >> mi->z;
729 data >> mi->o;
731 if(mi->HasMovementFlag(MOVEMENTFLAG_ONTRANSPORT))
733 if(!data.readPackGUID(mi->t_guid))
734 return;
736 data >> mi->t_x;
737 data >> mi->t_y;
738 data >> mi->t_z;
739 data >> mi->t_o;
740 data >> mi->t_time;
741 data >> mi->t_seat;
744 if((mi->HasMovementFlag(MovementFlags(MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_FLYING2))) || (mi->unk1 & 0x20))
746 data >> mi->s_pitch;
749 data >> mi->fallTime;
751 if(mi->HasMovementFlag(MOVEMENTFLAG_JUMPING))
753 data >> mi->j_unk;
754 data >> mi->j_sinAngle;
755 data >> mi->j_cosAngle;
756 data >> mi->j_xyspeed;
759 if(mi->HasMovementFlag(MOVEMENTFLAG_SPLINE))
761 data >> mi->u_unk1;
765 void WorldSession::WriteMovementInfo(WorldPacket *data, MovementInfo *mi)
767 data->appendPackGUID(mi->guid);
769 *data << mi->flags;
770 *data << mi->unk1;
771 *data << mi->time;
772 *data << mi->x;
773 *data << mi->y;
774 *data << mi->z;
775 *data << mi->o;
777 if(mi->HasMovementFlag(MOVEMENTFLAG_ONTRANSPORT))
779 data->appendPackGUID(mi->t_guid);
781 *data << mi->t_x;
782 *data << mi->t_y;
783 *data << mi->t_z;
784 *data << mi->t_o;
785 *data << mi->t_time;
786 *data << mi->t_seat;
789 if((mi->HasMovementFlag(MovementFlags(MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_FLYING2))) || (mi->unk1 & 0x20))
791 *data << mi->s_pitch;
794 *data << mi->fallTime;
796 if(mi->HasMovementFlag(MOVEMENTFLAG_JUMPING))
798 *data << mi->j_unk;
799 *data << mi->j_sinAngle;
800 *data << mi->j_cosAngle;
801 *data << mi->j_xyspeed;
804 if(mi->HasMovementFlag(MOVEMENTFLAG_SPLINE))
806 *data << mi->u_unk1;
810 void WorldSession::ReadAddonsInfo(WorldPacket &data)
812 if (data.rpos() + 4 > data.size())
813 return;
814 uint32 size;
815 data >> size;
817 if(!size)
818 return;
820 if(size > 0xFFFFF)
822 sLog.outError("WorldSession::ReadAddonsInfo addon info too big, size %u", size);
823 return;
826 uLongf uSize = size;
828 uint32 pos = data.rpos();
830 ByteBuffer addonInfo;
831 addonInfo.resize(size);
833 if (uncompress(const_cast<uint8*>(addonInfo.contents()), &uSize, const_cast<uint8*>(data.contents() + pos), data.size() - pos) == Z_OK)
835 uint32 addonsCount;
836 addonInfo >> addonsCount; // addons count
838 for(uint32 i = 0; i < addonsCount; ++i)
840 std::string addonName;
841 uint8 enabled;
842 uint32 crc, unk1;
844 // check next addon data format correctness
845 if(addonInfo.rpos()+1 > addonInfo.size())
846 return;
848 addonInfo >> addonName;
850 addonInfo >> enabled >> crc >> unk1;
852 sLog.outDebug("ADDON: Name: %s, Enabled: 0x%x, CRC: 0x%x, Unknown2: 0x%x", addonName.c_str(), enabled, crc, unk1);
854 m_addonsList.push_back(AddonInfo(addonName, enabled, crc));
857 uint32 unk2;
858 addonInfo >> unk2;
860 if(addonInfo.rpos() != addonInfo.size())
861 sLog.outDebug("packet under read!");
863 else
864 sLog.outError("Addon packet uncompress error!");
867 void WorldSession::SendAddonsInfo()
869 unsigned char tdata[256] =
871 0xC3, 0x5B, 0x50, 0x84, 0xB9, 0x3E, 0x32, 0x42, 0x8C, 0xD0, 0xC7, 0x48, 0xFA, 0x0E, 0x5D, 0x54,
872 0x5A, 0xA3, 0x0E, 0x14, 0xBA, 0x9E, 0x0D, 0xB9, 0x5D, 0x8B, 0xEE, 0xB6, 0x84, 0x93, 0x45, 0x75,
873 0xFF, 0x31, 0xFE, 0x2F, 0x64, 0x3F, 0x3D, 0x6D, 0x07, 0xD9, 0x44, 0x9B, 0x40, 0x85, 0x59, 0x34,
874 0x4E, 0x10, 0xE1, 0xE7, 0x43, 0x69, 0xEF, 0x7C, 0x16, 0xFC, 0xB4, 0xED, 0x1B, 0x95, 0x28, 0xA8,
875 0x23, 0x76, 0x51, 0x31, 0x57, 0x30, 0x2B, 0x79, 0x08, 0x50, 0x10, 0x1C, 0x4A, 0x1A, 0x2C, 0xC8,
876 0x8B, 0x8F, 0x05, 0x2D, 0x22, 0x3D, 0xDB, 0x5A, 0x24, 0x7A, 0x0F, 0x13, 0x50, 0x37, 0x8F, 0x5A,
877 0xCC, 0x9E, 0x04, 0x44, 0x0E, 0x87, 0x01, 0xD4, 0xA3, 0x15, 0x94, 0x16, 0x34, 0xC6, 0xC2, 0xC3,
878 0xFB, 0x49, 0xFE, 0xE1, 0xF9, 0xDA, 0x8C, 0x50, 0x3C, 0xBE, 0x2C, 0xBB, 0x57, 0xED, 0x46, 0xB9,
879 0xAD, 0x8B, 0xC6, 0xDF, 0x0E, 0xD6, 0x0F, 0xBE, 0x80, 0xB3, 0x8B, 0x1E, 0x77, 0xCF, 0xAD, 0x22,
880 0xCF, 0xB7, 0x4B, 0xCF, 0xFB, 0xF0, 0x6B, 0x11, 0x45, 0x2D, 0x7A, 0x81, 0x18, 0xF2, 0x92, 0x7E,
881 0x98, 0x56, 0x5D, 0x5E, 0x69, 0x72, 0x0A, 0x0D, 0x03, 0x0A, 0x85, 0xA2, 0x85, 0x9C, 0xCB, 0xFB,
882 0x56, 0x6E, 0x8F, 0x44, 0xBB, 0x8F, 0x02, 0x22, 0x68, 0x63, 0x97, 0xBC, 0x85, 0xBA, 0xA8, 0xF7,
883 0xB5, 0x40, 0x68, 0x3C, 0x77, 0x86, 0x6F, 0x4B, 0xD7, 0x88, 0xCA, 0x8A, 0xD7, 0xCE, 0x36, 0xF0,
884 0x45, 0x6E, 0xD5, 0x64, 0x79, 0x0F, 0x17, 0xFC, 0x64, 0xDD, 0x10, 0x6F, 0xF3, 0xF5, 0xE0, 0xA6,
885 0xC3, 0xFB, 0x1B, 0x8C, 0x29, 0xEF, 0x8E, 0xE5, 0x34, 0xCB, 0xD1, 0x2A, 0xCE, 0x79, 0xC3, 0x9A,
886 0x0D, 0x36, 0xEA, 0x01, 0xE0, 0xAA, 0x91, 0x20, 0x54, 0xF0, 0x72, 0xD8, 0x1E, 0xC7, 0x89, 0xD2
889 WorldPacket data(SMSG_ADDON_INFO, 4);
891 for(AddonsList::iterator itr = m_addonsList.begin(); itr != m_addonsList.end(); ++itr)
893 uint8 state = 2; // 2 is sent here
894 data << uint8(state);
896 uint8 unk1 = 1; // 1 is sent here
897 data << uint8(unk1);
898 if (unk1)
900 uint8 unk2 = (itr->CRC != 0x4c1c776d); // If addon is Standard addon CRC
901 data << uint8(unk2); // if 1, than add addon public signature
902 if (unk2) // if CRC is wrong, add public key (client need it)
903 data.append(tdata, sizeof(tdata));
905 data << uint32(0);
908 uint8 unk3 = 0; // 0 is sent here
909 data << uint8(unk3); // use <Addon>\<Addon>.url file or not
910 if (unk3)
912 // String, 256 (null terminated?)
913 data << uint8(0);
917 m_addonsList.clear();
919 uint32 count = 0;
920 data << uint32(count); // BannedAddons count
921 /*for(uint32 i = 0; i < count; ++i)
923 uint32
924 string (16 bytes)
925 string (16 bytes)
926 uint32
929 SendPacket(&data);
932 void WorldSession::SetPlayer( Player *plr )
934 _player = plr;
936 // set m_GUID that can be used while player loggined and later until m_playerRecentlyLogout not reset
937 if(_player)
938 m_GUIDLow = _player->GetGUIDLow();