diff options
Diffstat (limited to 'src/game/WorldSession.cpp')
-rw-r--r-- | src/game/WorldSession.cpp | 135 |
1 files changed, 134 insertions, 1 deletions
diff --git a/src/game/WorldSession.cpp b/src/game/WorldSession.cpp index 41164032a3a..f5515bf78c1 100644 --- a/src/game/WorldSession.cpp +++ b/src/game/WorldSession.cpp @@ -17,9 +17,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + /** \file \ingroup u2w */ + #include "WorldSocket.h" // must be first to make ACE happy with ACE includes in it #include "Common.h" #include "Database/DatabaseEnv.h" @@ -39,6 +41,7 @@ #include "SocialMgr.h" #include "zlib/zlib.h" #include "ScriptCalls.h" + /// WorldSession constructor WorldSession::WorldSession(uint32 id, WorldSocket *sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale) : LookingForGroup_auto_join(false), LookingForGroup_auto_add(false), m_muteTime(mute_time), @@ -54,12 +57,14 @@ m_latency(0), m_TutorialsChanged(false) loginDatabase.PExecute("UPDATE account SET online = 1 WHERE id = %u;", GetAccountId()); } } + /// WorldSession destructor WorldSession::~WorldSession() { ///- unload player if not unloaded if (_player) LogoutPlayer (true); + /// - If have unclosed socket, close it if (m_Socket) { @@ -67,41 +72,53 @@ WorldSession::~WorldSession() m_Socket->RemoveReference (); m_Socket = NULL; } + ///- empty incoming packet queue WorldPacket* packet; while(_recvQueue.next(packet)) delete packet; + loginDatabase.PExecute("UPDATE account SET online = 0 WHERE id = %u;", GetAccountId()); CharacterDatabase.PExecute("UPDATE characters SET online = 0 WHERE account = %u;", GetAccountId()); } + void WorldSession::SizeError(WorldPacket const& packet, uint32 size) const { sLog.outError("Client (account %u) send packet %s (%u) with size " SIZEFMTD " but expected %u (attempt crash server?), skipped", GetAccountId(),LookupOpcodeName(packet.GetOpcode()),packet.GetOpcode(),packet.size(),size); } + /// Get the player name char const* WorldSession::GetPlayerName() const { return GetPlayer() ? GetPlayer()->GetName() : "<none>"; } + /// Send a packet to the client void WorldSession::SendPacket(WorldPacket const* packet) { if (!m_Socket) return; + #ifdef TRINITY_DEBUG + // Code for network use statistic static uint64 sendPacketCount = 0; static uint64 sendPacketBytes = 0; + static time_t firstTime = time(NULL); static time_t lastTime = firstTime; // next 60 secs start time + static uint64 sendLastPacketCount = 0; static uint64 sendLastPacketBytes = 0; + time_t cur_time = time(NULL); + if((cur_time - lastTime) < 60) { sendPacketCount+=1; sendPacketBytes+=packet->size(); + sendLastPacketCount+=1; sendLastPacketBytes+=packet->size(); } @@ -111,19 +128,24 @@ void WorldSession::SendPacket(WorldPacket const* packet) uint64 fullTime = uint64(lastTime - firstTime); 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)); 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); + lastTime = cur_time; sendLastPacketCount = 1; sendLastPacketBytes = packet->wpos(); // wpos is real written size } + #endif // !TRINITY_DEBUG + if (m_Socket->SendPacket (*packet) == -1) m_Socket->CloseSocket (); } + /// Add an incoming packet to the queue void WorldSession::QueuePacket(WorldPacket* new_packet) { _recvQueue.add(new_packet); } + /// Logging helper for unexpected opcodes void WorldSession::LogUnexpectedOpcode(WorldPacket* packet, const char *reason) { @@ -132,6 +154,7 @@ void WorldSession::LogUnexpectedOpcode(WorldPacket* packet, const char *reason) packet->GetOpcode(), reason); } + /// Logging helper for unexpected opcodes void WorldSession::LogUnprocessedTail(WorldPacket *packet) { @@ -139,8 +162,10 @@ void WorldSession::LogUnprocessedTail(WorldPacket *packet) LookupOpcodeName(packet->GetOpcode()), packet->GetOpcode(), packet->rpos(),packet->wpos()); + packet->print_storage(); } + /// Update the WorldSession (triggered by World update) bool WorldSession::Update(uint32 /*diff*/) { @@ -154,6 +179,7 @@ bool WorldSession::Update(uint32 /*diff*/) LookupOpcodeName(packet->GetOpcode()), packet->GetOpcode()); #endif*/ + if(packet->GetOpcode() >= NUM_MSG_TYPES) { sLog.outError( "SESSION: received non-existed opcode %s (0x%.4X)", @@ -214,10 +240,12 @@ bool WorldSession::Update(uint32 /*diff*/) LogUnexpectedOpcode(packet, "the player not pass queue yet"); break; } + // single from authed time opcodes send in to after logout time // and before other STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT opcodes. if (packet->GetOpcode() != CMSG_SET_ACTIVE_VOICE_CHANNEL) m_playerRecentlyLogout = false; + (this->*opHandle.handler)(*packet); if (sLog.IsOutDebug() && packet->rpos() < packet->wpos()) LogUnprocessedTail(packet); @@ -241,33 +269,42 @@ bool WorldSession::Update(uint32 /*diff*/) } } } + delete packet; } + ///- Cleanup socket pointer if need if (m_Socket && m_Socket->IsClosed ()) { m_Socket->RemoveReference (); m_Socket = NULL; } + ///- If necessary, log the player out time_t currTime = time(NULL); if (!m_Socket || (ShouldLogOut(currTime) && !m_playerLoading)) LogoutPlayer(true); + if (!m_Socket) return false; //Will remove this session from the world session map + return true; } + /// %Log the player out void WorldSession::LogoutPlayer(bool Save) { // finish pending transfers before starting the logout while(_player && _player->IsBeingTeleportedFar()) HandleMoveWorldportAckOpcode(); + m_playerLogout = true; + if (_player) { if (uint64 lguid = GetPlayer()->GetLootGUID()) DoLootRelease(lguid); + ///- If the player just died before logging out, make him appear as a ghost //FIXME: logout must be delayed in case lost connection with client in time of combat if (_player->GetDeathTimer()) @@ -281,6 +318,7 @@ void WorldSession::LogoutPlayer(bool Save) _player->CombatStop(); _player->getHostilRefManager().setOnlineOfflineState(false); _player->RemoveAllAurasOnDeath(); + // build set of player who attack _player or who have pet attacking of _player std::set<Player*> aset; for(Unit::AttackerSet::const_iterator itr = _player->getAttackers().begin(); itr != _player->getAttackers().end(); ++itr) @@ -295,13 +333,16 @@ void WorldSession::LogoutPlayer(bool Save) if((*itr)->GetTypeId()==TYPEID_PLAYER) aset.insert((Player*)(*itr)); } + _player->SetPvPDeath(!aset.empty()); _player->KillPlayer(); _player->BuildPlayerRepop(); _player->RepopAtGraveyard(); + // give honor to all attackers from set like group case for(std::set<Player*>::const_iterator itr = aset.begin(); itr != aset.end(); ++itr) (*itr)->RewardHonor(_player,aset.size()); + // give bg rewards and update counters like kill by first from attackers // this can't be called for all attackers. if(!aset.empty()) @@ -320,10 +361,13 @@ void WorldSession::LogoutPlayer(bool Save) //drop a flag if player is carrying it if(BattleGround *bg = _player->GetBattleGround()) bg->EventPlayerLoggedOut(_player); + ///- Teleport to home if the player is in an invalid instance if(!_player->m_InstanceValid && !_player->isGameMaster()) _player->TeleportTo(_player->m_homebindMapId, _player->m_homebindX, _player->m_homebindY, _player->m_homebindZ, _player->GetOrientation()); + sOutdoorPvPMgr.HandlePlayerLeaveZone(_player,_player->GetZoneId()); + for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) { if(BattleGroundQueueTypeId bgQueueTypeId = _player->GetBattleGroundQueueTypeId(i)) @@ -332,16 +376,19 @@ void WorldSession::LogoutPlayer(bool Save) sBattleGroundMgr.m_BattleGroundQueues[ bgQueueTypeId ].RemovePlayer(_player->GetGUID(), true); } } + // Repop at GraveYard or other player far teleport will prevent saving player because of not present map // Teleport player immediately for correct player save while(_player->IsBeingTeleportedFar()) HandleMoveWorldportAckOpcode(); + ///- If the player is in a guild, update the guild roster and broadcast a logout message to other guild members Guild *guild = objmgr.GetGuildById(_player->GetGuildId()); if(guild) { guild->SetMemberStats(_player->GetGUID()); guild->UpdateLogoutTime(_player->GetGUID()); + WorldPacket data(SMSG_GUILD_EVENT, (1+1+12+8)); // name limited to 12 in character table. data<<(uint8)GE_SIGNED_OFF; data<<(uint8)1; @@ -349,8 +396,10 @@ void WorldSession::LogoutPlayer(bool Save) data<<_player->GetGUID(); guild->BroadcastPacket(&data); } + ///- Remove pet _player->RemovePet(NULL,PET_SAVE_AS_CURRENT, true); + ///- empty buyback items and save the player in the database // some save parts only correctly work in case player present in map/player_lists (pets, etc) if(Save) @@ -365,20 +414,26 @@ void WorldSession::LogoutPlayer(bool Save) } _player->SaveToDB(); } + ///- Leave all channels before player delete... _player->CleanupChannels(); + ///- If the player is in a group (or invited), remove him. If the group if then only 1 person, disband the group. _player->UninviteFromGroup(); + // remove player from the group if he is: // a) in group; b) not in raid group; c) logging out normally (not being kicked or disconnected) if(_player->GetGroup() && !_player->GetGroup()->isRaidGroup() && m_Socket) _player->RemoveFromGroup(); + ///- Send update to group if(_player->GetGroup()) _player->GetGroup()->SendUpdate(); + ///- Broadcast a logout message to the player's friends sSocialMgr.SendFriendStatus(_player, FRIEND_OFFLINE, _player->GetGUIDLow(), true); sSocialMgr.RemovePlayerSocial (_player->GetGUIDLow ()); + ///- Remove the player from the world // the player may not be in the world when logging out // e.g if he got disconnected during a transfer to another map @@ -387,42 +442,52 @@ void WorldSession::LogoutPlayer(bool Save) Map* _map = _player->GetMap(); _map->Remove(_player, true); SetPlayer(NULL); // deleted in Remove call + ///- Send the 'logout complete' packet to the client WorldPacket data( SMSG_LOGOUT_COMPLETE, 0 ); SendPacket( &data ); + ///- Since each account can only have one online character at any given time, ensure all characters for active account are marked as offline //No SQL injection as AccountId is uint32 CharacterDatabase.PExecute("UPDATE characters SET online = 0 WHERE account = '%u'", GetAccountId()); sLog.outDebug( "SESSION: Sent SMSG_LOGOUT_COMPLETE Message" ); } + //Hook for OnLogout Event Script->OnLogout(_player); + m_playerLogout = false; m_playerRecentlyLogout = true; LogoutRequest(0); } + /// Kick a player out of the World void WorldSession::KickPlayer() { if (m_Socket) m_Socket->CloseSocket (); } + /// Cancel channeling handler + void WorldSession::SendAreaTriggerMessage(const char* Text, ...) { va_list ap; char szStr [1024]; szStr[0] = '\0'; + va_start(ap, Text); vsnprintf( szStr, 1024, Text, ap ); va_end(ap); + uint32 length = strlen(szStr)+1; WorldPacket data(SMSG_AREA_TRIGGER_MESSAGE, 4+length); data << length; data << szStr; SendPacket(&data); } + void WorldSession::SendNotification(const char *format,...) { if(format) @@ -433,11 +498,13 @@ void WorldSession::SendNotification(const char *format,...) va_start(ap, format); vsnprintf( szStr, 1024, format, ap ); va_end(ap); + WorldPacket data(SMSG_NOTIFICATION, (strlen(szStr)+1)); data << szStr; SendPacket(&data); } } + void WorldSession::SendNotification(int32 string_id,...) { char const* format = GetTrinityString(string_id); @@ -449,45 +516,53 @@ void WorldSession::SendNotification(int32 string_id,...) va_start(ap, string_id); vsnprintf( szStr, 1024, format, ap ); va_end(ap); + WorldPacket data(SMSG_NOTIFICATION, (strlen(szStr)+1)); data << szStr; SendPacket(&data); } } + void WorldSession::SendSetPhaseShift(uint32 PhaseShift) { WorldPacket data(SMSG_SET_PHASE_SHIFT, 4); data << uint32(PhaseShift); SendPacket(&data); } + const char * WorldSession::GetTrinityString( int32 entry ) const { return objmgr.GetTrinityString(entry,GetSessionDbLocaleIndex()); } + void WorldSession::Handle_NULL( WorldPacket& recvPacket ) { sLog.outError( "SESSION: received unhandled opcode %s (0x%.4X)", LookupOpcodeName(recvPacket.GetOpcode()), recvPacket.GetOpcode()); } + void WorldSession::Handle_EarlyProccess( WorldPacket& recvPacket ) { sLog.outError( "SESSION: received opcode %s (0x%.4X) that must be processed in WorldSocket::OnRead", LookupOpcodeName(recvPacket.GetOpcode()), recvPacket.GetOpcode()); } + void WorldSession::Handle_ServerSide( WorldPacket& recvPacket ) { sLog.outError( "SESSION: received server-side opcode %s (0x%.4X)", LookupOpcodeName(recvPacket.GetOpcode()), recvPacket.GetOpcode()); } + void WorldSession::Handle_Deprecated( WorldPacket& recvPacket ) { sLog.outError( "SESSION: received deprecated opcode %s (0x%.4X)", LookupOpcodeName(recvPacket.GetOpcode()), recvPacket.GetOpcode()); } + void WorldSession::SendAuthWaitQue(uint32 position) { if(position == 0) @@ -504,6 +579,7 @@ void WorldSession::SendAuthWaitQue(uint32 position) SendPacket(&packet); } } + void WorldSession::LoadGlobalAccountData() { LoadAccountData( @@ -511,16 +587,20 @@ void WorldSession::LoadGlobalAccountData() GLOBAL_CACHE_MASK ); } + void WorldSession::LoadAccountData(QueryResult* result, uint32 mask) { for (uint32 i = 0; i < NUM_ACCOUNT_DATA_TYPES; ++i) if (mask & (1 << i)) m_accountData[i] = AccountData(); + if(!result) return; + do { Field *fields = result->Fetch(); + uint32 type = fields[0].GetUInt32(); if (type >= NUM_ACCOUNT_DATA_TYPES) { @@ -528,22 +608,28 @@ void WorldSession::LoadAccountData(QueryResult* result, uint32 mask) mask == GLOBAL_CACHE_MASK ? "account_data" : "character_account_data", type); continue; } + if ((mask & (1 << type))==0) { sLog.outError("Table `%s` have non appropriate for table account data type (%u), ignore.", mask == GLOBAL_CACHE_MASK ? "account_data" : "character_account_data", type); continue; } + m_accountData[type].Time = fields[1].GetUInt32(); m_accountData[type].Data = fields[2].GetCppString(); + } while (result->NextRow()); + delete result; } + void WorldSession::SetAccountData(AccountDataType type, time_t time_, std::string data) { if ((1 << type) & GLOBAL_CACHE_MASK) { uint32 acc = GetAccountId(); + CharacterDatabase.BeginTransaction (); CharacterDatabase.PExecute("DELETE FROM account_data WHERE account='%u' AND type='%u'", acc, type); CharacterDatabase.escape_string(data); @@ -555,15 +641,18 @@ void WorldSession::SetAccountData(AccountDataType type, time_t time_, std::strin // _player can be NULL and packet received after logout but m_GUID still store correct guid if(!m_GUIDLow) return; + CharacterDatabase.BeginTransaction (); CharacterDatabase.PExecute("DELETE FROM character_account_data WHERE guid='%u' AND type='%u'", m_GUIDLow, type); CharacterDatabase.escape_string(data); CharacterDatabase.PExecute("INSERT INTO character_account_data VALUES ('%u','%u','%u','%s')", m_GUIDLow, type, (uint32)time_, data.c_str()); CharacterDatabase.CommitTransaction (); } + m_accountData[type].Time = time_; m_accountData[type].Data = data; } + void WorldSession::SendAccountDataTimes() { WorldPacket data( SMSG_ACCOUNT_DATA_TIMES, 4+1+8*4 ); // changed in WotLK @@ -573,24 +662,31 @@ void WorldSession::SendAccountDataTimes() data << uint32(m_accountData[i].Time); // also unix time SendPacket(&data); } + void WorldSession::LoadTutorialsData() { for ( int aX = 0 ; aX < 8 ; ++aX ) m_Tutorials[ aX ] = 0; + QueryResult *result = CharacterDatabase.PQuery("SELECT tut0,tut1,tut2,tut3,tut4,tut5,tut6,tut7 FROM character_tutorial WHERE account = '%u'", GetAccountId()); + if(result) { do { Field *fields = result->Fetch(); + for (int iI = 0; iI < 8; ++iI) m_Tutorials[iI] = fields[iI].GetUInt32(); } while( result->NextRow() ); + delete result; } + m_TutorialsChanged = false; } + void WorldSession::SendTutorialsData() { WorldPacket data(SMSG_TUTORIAL_FLAGS, 4*8); @@ -598,10 +694,12 @@ void WorldSession::SendTutorialsData() data << m_Tutorials[i]; SendPacket(&data); } + void WorldSession::SaveTutorialsData() { if(!m_TutorialsChanged) return; + uint32 Rows=0; // it's better than rebuilding indexes multiple times QueryResult *result = CharacterDatabase.PQuery("SELECT count(*) AS r FROM character_tutorial WHERE account = '%u'", GetAccountId()); @@ -610,6 +708,7 @@ void WorldSession::SaveTutorialsData() Rows = result->Fetch()[0].GetUInt32(); delete result; } + if (Rows) { 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'", @@ -619,8 +718,10 @@ void WorldSession::SaveTutorialsData() { 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]); } + m_TutorialsChanged = false; } + void WorldSession::ReadMovementInfo(WorldPacket &data, MovementInfo *mi) { data >> mi->flags; @@ -630,10 +731,12 @@ void WorldSession::ReadMovementInfo(WorldPacket &data, MovementInfo *mi) data >> mi->y; data >> mi->z; data >> mi->o; + if(mi->flags & MOVEMENTFLAG_ONTRANSPORT) { if(!data.readPackGUID(mi->t_guid)) return; + data >> mi->t_x; data >> mi->t_y; data >> mi->t_z; @@ -641,11 +744,14 @@ void WorldSession::ReadMovementInfo(WorldPacket &data, MovementInfo *mi) data >> mi->t_time; data >> mi->t_seat; } + if((mi->flags & (MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_FLYING)) || (mi->unk1 & 0x20)) { data >> mi->s_pitch; } + data >> mi->fallTime; + if(mi->flags & MOVEMENTFLAG_JUMPING) { data >> mi->j_zspeed; @@ -653,53 +759,70 @@ void WorldSession::ReadMovementInfo(WorldPacket &data, MovementInfo *mi) data >> mi->j_cosAngle; data >> mi->j_xyspeed; } + if(mi->flags & MOVEMENTFLAG_SPLINE) { data >> mi->u_unk1; } } + void WorldSession::ReadAddonsInfo(WorldPacket &data) { if (data.rpos() + 4 > data.size()) return; uint32 size; data >> size; + if(!size) return; + if(size > 0xFFFFF) { sLog.outError("WorldSession::ReadAddonsInfo addon info too big, size %u", size); return; } + uLongf uSize = size; + uint32 pos = data.rpos(); + ByteBuffer addonInfo; addonInfo.resize(size); + if (uncompress(const_cast<uint8*>(addonInfo.contents()), &uSize, const_cast<uint8*>(data.contents() + pos), data.size() - pos) == Z_OK) { uint32 addonsCount; addonInfo >> addonsCount; // addons count + for(uint32 i = 0; i < addonsCount; ++i) { std::string addonName; uint8 enabled; uint32 crc, unk1; + // check next addon data format correctness if(addonInfo.rpos()+1 > addonInfo.size()) return; + addonInfo >> addonName; + addonInfo >> enabled >> crc >> unk1; + sLog.outDebug("ADDON: Name: %s, Enabled: 0x%x, CRC: 0x%x, Unknown2: 0x%x", addonName.c_str(), enabled, crc, unk1); + m_addonsList.push_back(AddonInfo(addonName, enabled, crc)); } + uint32 unk2; addonInfo >> unk2; + if(addonInfo.rpos() != addonInfo.size()) sLog.outDebug("packet under read!"); } else sLog.outError("Addon packet uncompress error!"); } + void WorldSession::SendAddonsInfo() { unsigned char tdata[256] = @@ -721,11 +844,14 @@ void WorldSession::SendAddonsInfo() 0xC3, 0xFB, 0x1B, 0x8C, 0x29, 0xEF, 0x8E, 0xE5, 0x34, 0xCB, 0xD1, 0x2A, 0xCE, 0x79, 0xC3, 0x9A, 0x0D, 0x36, 0xEA, 0x01, 0xE0, 0xAA, 0x91, 0x20, 0x54, 0xF0, 0x72, 0xD8, 0x1E, 0xC7, 0x89, 0xD2 }; + WorldPacket data(SMSG_ADDON_INFO, 4); + for(AddonsList::iterator itr = m_addonsList.begin(); itr != m_addonsList.end(); ++itr) { uint8 state = 2; // 2 is sent here data << uint8(state); + uint8 unk1 = 1; // 1 is sent here data << uint8(unk1); if (unk1) @@ -734,8 +860,10 @@ void WorldSession::SendAddonsInfo() data << uint8(unk2); if (unk2) // if CRC is wrong, add public key (client need it) data.append(tdata, sizeof(tdata)); + data << uint32(0); } + uint8 unk3 = 0; // 0 is sent here data << uint8(unk3); if (unk3) @@ -744,7 +872,9 @@ void WorldSession::SendAddonsInfo() data << uint8(0); } } + m_addonsList.clear(); + uint32 count = 0; data << uint32(count); /*for(uint32 i = 0; i < count; ++i) @@ -754,12 +884,15 @@ void WorldSession::SendAddonsInfo() string (16 bytes) uint32 }*/ + SendPacket(&data); } + void WorldSession::SetPlayer( Player *plr ) { _player = plr; + // set m_GUID that can be used while player loggined and later until m_playerRecentlyLogout not reset if(_player) m_GUIDLow = _player->GetGUIDLow(); -} +}
\ No newline at end of file |