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
23 #include "WorldSocket.h" // must be first to make ACE happy with ACE includes in it
25 #include "Database/DatabaseEnv.h"
28 #include "WorldPacket.h"
29 #include "WorldSession.h"
31 #include "ObjectMgr.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)
51 m_Address
= sock
->GetRemoteAddress ();
52 sock
->AddReference ();
56 /// WorldSession destructor
57 WorldSession::~WorldSession()
59 ///- unload player if not unloaded
63 /// - If have unclosed socket, close it
66 m_Socket
->CloseSocket ();
67 m_Socket
->RemoveReference ();
71 ///- empty incoming packet queue
73 while(_recvQueue
.next(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
)
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)
112 sendPacketBytes
+=packet
->size();
114 sendLastPacketCount
+=1;
115 sendLastPacketBytes
+=packet
->size();
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
);
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()),
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()),
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
165 while (_recvQueue
.next(packet
) && m_Socket
&& !m_Socket
->IsClosed ())
168 sLog.outError( "MOEP: %s (0x%.4X)",
169 LookupOpcodeName(packet->GetOpcode()),
170 packet->GetOpcode());
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());
181 OpcodeHandler
& opHandle
= opcodeTable
[packet
->GetOpcode()];
184 switch (opHandle
.status
)
186 case STATUS_LOGGEDIN
:
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
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");
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
);
214 case STATUS_TRANSFER
:
216 LogUnexpectedOpcode(packet
, "the player has not logged in yet");
217 else if(_player
->IsInWorld())
218 LogUnexpectedOpcode(packet
, "the player is still in world");
221 (this->*opHandle
.handler
)(*packet
);
222 if (sLog
.IsOutDebug() && packet
->rpos() < packet
->wpos())
223 LogUnprocessedTail(packet
);
227 // prevent cheating with skip queue wait
230 LogUnexpectedOpcode(packet
, "the player not pass queue yet");
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
);
244 sLog
.outError( "SESSION: received not allowed opcode %s (0x%.4X)",
245 LookupOpcodeName(packet
->GetOpcode()),
246 packet
->GetOpcode());
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:");
265 ///- Cleanup socket pointer if need
266 if (m_Socket
&& m_Socket
->IsClosed ())
268 m_Socket
->RemoveReference ();
272 ///- If necessary, log the player out
273 time_t currTime
= time(NULL
);
274 if (!m_Socket
|| (ShouldLogOut(currTime
) && !m_playerLoading
))
278 return false; //Will remove this session from the world session map
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;
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
318 if(owner
->GetTypeId()==TYPEID_PLAYER
)
319 aset
.insert((Player
*)owner
);
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.
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());
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
;
391 data
<<_player
->GetName();
392 data
<<_player
->GetGUID();
393 guild
->BroadcastPacket(&data
);
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)
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);
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 );
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'",
449 sLog
.outDebug( "SESSION: Sent SMSG_LOGOUT_COMPLETE Message" );
452 m_playerLogout
= false;
453 m_playerRecentlyLogout
= true;
457 /// Kick a player out of the World
458 void WorldSession::KickPlayer()
461 m_Socket
->CloseSocket ();
464 /// Cancel channeling handler
466 void WorldSession::SendAreaTriggerMessage(const char* Text
, ...)
473 vsnprintf( szStr
, 1024, Text
, ap
);
476 uint32 length
= strlen(szStr
)+1;
477 WorldPacket
data(SMSG_AREA_TRIGGER_MESSAGE
, 4+length
);
483 void WorldSession::SendNotification(const char *format
,...)
490 va_start(ap
, format
);
491 vsnprintf( szStr
, 1024, format
, ap
);
494 WorldPacket
data(SMSG_NOTIFICATION
, (strlen(szStr
)+1));
500 void WorldSession::SendNotification(int32 string_id
,...)
502 char const* format
= GetMangosString(string_id
);
508 va_start(ap
, string_id
);
509 vsnprintf( szStr
, 1024, format
, ap
);
512 WorldPacket
data(SMSG_NOTIFICATION
, (strlen(szStr
)+1));
518 void WorldSession::SendSetPhaseShift(uint32 PhaseShift
)
520 WorldPacket
data(SMSG_SET_PHASE_SHIFT
, 4);
521 data
<< uint32(PhaseShift
);
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
)
562 WorldPacket
packet( SMSG_AUTH_RESPONSE
, 1 );
563 packet
<< uint8( AUTH_OK
);
568 WorldPacket
packet( SMSG_AUTH_RESPONSE
, 5 );
569 packet
<< uint8( AUTH_WAIT_QUEUE
);
570 packet
<< uint32 (position
);
575 void WorldSession::LoadGlobalAccountData()
578 CharacterDatabase
.PQuery("SELECT type, time, data FROM account_data WHERE account='%u'", GetAccountId()),
583 void WorldSession::LoadAccountData(QueryResult
* result
, uint32 mask
)
585 for (uint32 i
= 0; i
< NUM_ACCOUNT_DATA_TYPES
; ++i
)
587 m_accountData
[i
] = AccountData();
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
);
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
);
611 m_accountData
[type
].Time
= fields
[1].GetUInt32();
612 m_accountData
[type
].Data
= fields
[2].GetCppString();
614 } while (result
->NextRow());
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 ();
633 // _player can be NULL and packet received after logout but m_GUID still store correct guid
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
653 for(int i
= 0; i
< NUM_ACCOUNT_DATA_TYPES
; ++i
)
654 data
<< uint32(m_accountData
[i
].Time
); // also unix time
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());
669 Field
*fields
= result
->Fetch();
671 for (int iI
= 0; iI
< 8; ++iI
)
672 m_Tutorials
[iI
] = fields
[iI
].GetUInt32();
674 while( result
->NextRow() );
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
];
690 void WorldSession::SaveTutorialsData()
692 if(!m_TutorialsChanged
)
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());
700 Rows
= result
->Fetch()[0].GetUInt32();
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());
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
)
727 if(mi
->HasMovementFlag(MOVEMENTFLAG_ONTRANSPORT
))
729 if(!data
.readPackGUID(mi
->t_guid
))
740 if((mi
->HasMovementFlag(MovementFlags(MOVEMENTFLAG_SWIMMING
| MOVEMENTFLAG_FLYING2
))) || (mi
->unk1
& 0x20))
745 data
>> mi
->fallTime
;
747 if(mi
->HasMovementFlag(MOVEMENTFLAG_JUMPING
))
750 data
>> mi
->j_sinAngle
;
751 data
>> mi
->j_cosAngle
;
752 data
>> mi
->j_xyspeed
;
755 if(mi
->HasMovementFlag(MOVEMENTFLAG_SPLINE
))
761 void WorldSession::ReadAddonsInfo(WorldPacket
&data
)
763 if (data
.rpos() + 4 > data
.size())
773 sLog
.outError("WorldSession::ReadAddonsInfo addon info too big, size %u", 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
)
787 addonInfo
>> addonsCount
; // addons count
789 for(uint32 i
= 0; i
< addonsCount
; ++i
)
791 std::string addonName
;
795 // check next addon data format correctness
796 if(addonInfo
.rpos()+1 > addonInfo
.size())
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
));
811 if(addonInfo
.rpos() != addonInfo
.size())
812 sLog
.outDebug("packet under read!");
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
851 uint8 unk2
= (itr
->CRC
!= 0x4c1c776d); // If addon is Standard addon CRC
853 if (unk2
) // if CRC is wrong, add public key (client need it)
854 data
.append(tdata
, sizeof(tdata
));
859 uint8 unk3
= 0; // 0 is sent here
863 // String, 256 (null terminated?)
868 m_addonsList
.clear();
871 data
<< uint32(count
);
872 /*for(uint32 i = 0; i < count; ++i)
883 void WorldSession::SetPlayer( Player
*plr
)
887 // set m_GUID that can be used while player loggined and later until m_playerRecentlyLogout not reset
889 m_GUIDLow
= _player
->GetGUIDLow();