root/trunk/src/game/WorldSession.cpp @ 174

Revision 174, 18.1 kB (checked in by yumileroy, 17 years ago)

[svn] Implemented player on player and player on creature possession:
* Implemented packet and vision forwarding through possessed units
* Added new OnPossess? script call alerting scripts on when possession is applied/removed
* Moved fall damage and fall under map calculations into the Player class
* Added new PossessedAI that is applied only while possession on creature is active
* Implemented summon possessed spell effect
* Fixed Eyes of the Beast

Original author: gvcoman
Date: 2008-11-05 20:51:05-06:00

Line 
1/*
2 * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
3 *
4 * Copyright (C) 2008 Trinity <http://www.trinitycore.org/>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21/** \file
22    \ingroup u2w
23*/
24
25#include "WorldSocket.h"
26#include "Common.h"
27#include "Database/DatabaseEnv.h"
28#include "Log.h"
29#include "Opcodes.h"
30#include "WorldSocket.h"
31#include "WorldPacket.h"
32#include "WorldSession.h"
33#include "Player.h"
34#include "ObjectMgr.h"
35#include "Group.h"
36#include "Guild.h"
37#include "World.h"
38#include "MapManager.h"
39#include "ObjectAccessor.h"
40#include "BattleGroundMgr.h"
41#include "OutdoorPvPMgr.h"
42#include "Language.h"                                       // for CMSG_CANCEL_MOUNT_AURA handler
43#include "Chat.h"
44#include "SocialMgr.h"
45
46/// WorldSession constructor
47WorldSession::WorldSession(uint32 id, WorldSocket *sock, uint32 sec, uint8 expansion, time_t mute_time, LocaleConstant locale) :
48LookingForGroup_auto_join(false), LookingForGroup_auto_add(false), m_muteTime(mute_time),
49_player(NULL), m_Socket(sock),_security(sec), _accountId(id), m_expansion(expansion),
50m_sessionDbcLocale(sWorld.GetAvailableDbcLocale(locale)), m_sessionDbLocaleIndex(objmgr.GetIndexForLocale(locale)),
51_logoutTime(0), m_playerLoading(false), m_playerLogout(false), m_playerRecentlyLogout(false), m_latency(0)
52{
53   if (sock)
54   {
55           m_Address = sock->GetRemoteAddress ();
56           sock->AddReference ();
57   }
58}
59
60/// WorldSession destructor
61WorldSession::~WorldSession()
62{
63    ///- unload player if not unloaded
64    if(_player)
65        LogoutPlayer(true);
66
67    /// - If have unclosed socket, close it
68  if (m_Socket)
69    {
70      m_Socket->CloseSocket ();   
71      m_Socket->RemoveReference ();
72      m_Socket = NULL;
73    }
74
75    ///- empty incoming packet queue
76    while(!_recvQueue.empty())
77    {
78        WorldPacket *packet = _recvQueue.next();
79        delete packet;
80    }
81   
82}
83
84void WorldSession::SizeError(WorldPacket const& packet, uint32 size) const
85{
86    sLog.outError("Client (account %u) send packet %s (%u) with size %u but expected %u (attempt crash server?), skipped",
87        GetAccountId(),LookupOpcodeName(packet.GetOpcode()),packet.GetOpcode(),packet.size(),size);
88}
89
90/// Get the player name
91char const* WorldSession::GetPlayerName() const
92{
93    return GetPlayer() ? GetPlayer()->GetName() : "<none>";
94}
95
96/// Send a packet to the client
97void WorldSession::SendPacket(WorldPacket const* packet)
98{
99    if (!m_Socket)
100        return;
101
102    #ifdef TRINITY_DEBUG
103
104    // Code for network use statistic
105    static uint64 sendPacketCount = 0;
106    static uint64 sendPacketBytes = 0;
107
108    static time_t firstTime = time(NULL);
109    static time_t lastTime = firstTime;                     // next 60 secs start time
110
111    static uint64 sendLastPacketCount = 0;
112    static uint64 sendLastPacketBytes = 0;
113
114    time_t cur_time = time(NULL);
115
116    if((cur_time - lastTime) < 60)
117    {
118        sendPacketCount+=1;
119        sendPacketBytes+=packet->size();
120
121        sendLastPacketCount+=1;
122        sendLastPacketBytes+=packet->size();
123    }
124    else
125    {
126        uint64 minTime = uint64(cur_time - lastTime);
127        uint64 fullTime = uint64(lastTime - firstTime);
128        sLog.outDetail("Send all time packets count: " I64FMTD " bytes: " I64FMTD " avr.count/sec: %f avr.bytes/sec: %f time: %u",sendPacketCount,sendPacketBytes,float(sendPacketCount)/fullTime,float(sendPacketBytes)/fullTime,uint32(fullTime));
129        sLog.outDetail("Send last min packets count: " I64FMTD " bytes: " I64FMTD " avr.count/sec: %f avr.bytes/sec: %f",sendLastPacketCount,sendLastPacketBytes,float(sendLastPacketCount)/minTime,float(sendLastPacketBytes)/minTime);
130
131        lastTime = cur_time;
132        sendLastPacketCount = 1;
133        sendLastPacketBytes = packet->wpos();               // wpos is real written size
134    }
135
136        #endif                                                  // !TRINITY_DEBUG
137
138        if (m_Socket->SendPacket (*packet) == -1)
139                m_Socket->CloseSocket ();
140}
141
142/// Add an incoming packet to the queue
143void WorldSession::QueuePacket(WorldPacket* new_packet)
144{
145    _recvQueue.add(new_packet);
146}
147
148/// Logging helper for unexpected opcodes
149void WorldSession::logUnexpectedOpcode(WorldPacket* packet, const char *reason)
150{
151    sLog.outError( "SESSION: received unexpected opcode %s (0x%.4X) %s",
152        LookupOpcodeName(packet->GetOpcode()),
153        packet->GetOpcode(),
154        reason);
155}
156
157/// Update the WorldSession (triggered by World update)
158bool WorldSession::Update(uint32 /*diff*/)
159{
160  if (m_Socket && m_Socket->IsClosed ())
161  {
162        m_Socket->RemoveReference ();
163        m_Socket = NULL;
164  }
165 
166    WorldPacket *packet;
167
168    ///- Retrieve packets from the receive queue and call the appropriate handlers
169    /// \todo Is there a way to consolidate the OpcondeHandlerTable and the g_worldOpcodeNames to only maintain 1 list?
170    /// answer : there is a way, but this is better, because it would use redundant RAM
171    while (!_recvQueue.empty())
172    {
173        packet = _recvQueue.next();
174
175        /*#if 1
176        sLog.outError( "MOEP: %s (0x%.4X)",
177                        LookupOpcodeName(packet->GetOpcode()),
178                        packet->GetOpcode());
179        #endif*/
180
181        if(packet->GetOpcode() >= NUM_MSG_TYPES)
182        {
183            sLog.outError( "SESSION: received non-existed opcode %s (0x%.4X)",
184                LookupOpcodeName(packet->GetOpcode()),
185                packet->GetOpcode());
186        }
187        else
188        {
189            OpcodeHandler& opHandle = opcodeTable[packet->GetOpcode()];
190            switch (opHandle.status)
191            {
192                case STATUS_LOGGEDIN:
193                    if(!_player)
194                    {
195                        // skip STATUS_LOGGEDIN opcode unexpected errors if player logout sometime ago - this can be network lag delayed packets
196                        if(!m_playerRecentlyLogout)
197                            logUnexpectedOpcode(packet, "the player has not logged in yet");
198                    }
199                    else if(_player->IsInWorld())
200                        (this->*opHandle.handler)(*packet);
201                    // lag can cause STATUS_LOGGEDIN opcodes to arrive after the player started a transfer
202                    break;
203                case STATUS_TRANSFER_PENDING:
204                    if(!_player)
205                        logUnexpectedOpcode(packet, "the player has not logged in yet");
206                    else if(_player->IsInWorld())
207                        logUnexpectedOpcode(packet, "the player is still in world");
208                    else
209                        (this->*opHandle.handler)(*packet);
210                    break;
211                case STATUS_AUTHED:
212                    m_playerRecentlyLogout = false;
213                    (this->*opHandle.handler)(*packet);
214                    break;
215                case STATUS_NEVER:
216                    sLog.outError( "SESSION: received not allowed opcode %s (0x%.4X)",
217                        LookupOpcodeName(packet->GetOpcode()),
218                        packet->GetOpcode());
219                    break;
220            }
221        }
222
223        delete packet;
224    }
225
226    ///- If necessary, log the player out
227    time_t currTime = time(NULL);
228    if (!m_Socket || (ShouldLogOut(currTime) && !m_playerLoading))
229        LogoutPlayer(true);
230
231    if (!m_Socket)
232        return false;                                       //Will remove this session from the world session map
233
234    return true;
235}
236
237/// %Log the player out
238void WorldSession::LogoutPlayer(bool Save)
239{
240    // finish pending transfers before starting the logout
241    while(_player && _player->IsBeingTeleported())
242        HandleMoveWorldportAckOpcode();
243
244    m_playerLogout = true;
245
246    if (_player)
247    {
248        if (uint64 lguid = GetPlayer()->GetLootGUID())
249            DoLootRelease(lguid);
250           
251        ///- If the player just died before logging out, make him appear as a ghost
252        //FIXME: logout must be delayed in case lost connection with client in time of combat
253        if (_player->GetDeathTimer())
254        {
255            _player->getHostilRefManager().deleteReferences();
256            _player->BuildPlayerRepop();
257            _player->RepopAtGraveyard();
258        }
259        else if (!_player->getAttackers().empty())
260        {
261            _player->CombatStop();
262            _player->getHostilRefManager().setOnlineOfflineState(false);
263            _player->RemoveAllAurasOnDeath();
264
265            // build set of player who attack _player or who have pet attacking of _player
266            std::set<Player*> aset;
267            for(Unit::AttackerSet::const_iterator itr = _player->getAttackers().begin(); itr != _player->getAttackers().end(); ++itr)
268            {
269                Unit* owner = (*itr)->GetOwner();           // including player controlled case
270                if(owner)
271                {
272                    if(owner->GetTypeId()==TYPEID_PLAYER)
273                        aset.insert((Player*)owner);
274                }
275                else
276                if((*itr)->GetTypeId()==TYPEID_PLAYER)
277                    aset.insert((Player*)(*itr));
278            }
279
280            _player->SetPvPDeath(!aset.empty());
281            _player->KillPlayer();
282            _player->BuildPlayerRepop();
283            _player->RepopAtGraveyard();
284
285            // give honor to all attackers from set like group case
286            for(std::set<Player*>::const_iterator itr = aset.begin(); itr != aset.end(); ++itr)
287                (*itr)->RewardHonor(_player, aset.size(), -1, true);
288
289            // give bg rewards and update counters like kill by first from attackers
290            // this can't be called for all attackers.
291            if(!aset.empty())
292                if(BattleGround *bg = _player->GetBattleGround())
293                    bg->HandleKillPlayer(_player,*aset.begin());
294        }
295        else if(_player->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION))
296        {
297            // this will kill character by SPELL_AURA_SPIRIT_OF_REDEMPTION
298            _player->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT);
299            //_player->SetDeathPvP(*); set at SPELL_AURA_SPIRIT_OF_REDEMPTION apply time
300            _player->KillPlayer();
301            _player->BuildPlayerRepop();
302            _player->RepopAtGraveyard();
303        }
304
305        ///- Remove player from battleground (teleport to entrance)
306        if(_player->InBattleGround())
307            _player->LeaveBattleground();
308
309        sOutdoorPvPMgr.HandlePlayerLeaveZone(_player,_player->GetZoneId());
310
311        for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++)
312        {
313            if(int32 bgTypeId = _player->GetBattleGroundQueueId(i))
314            {
315                _player->RemoveBattleGroundQueueId(bgTypeId);
316                sBattleGroundMgr.m_BattleGroundQueues[ bgTypeId ].RemovePlayer(_player->GetGUID(), true);
317            }
318        }
319
320        ///- Reset the online field in the account table
321        // 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
322        //No SQL injection as AccountID is uint32
323        loginDatabase.PExecute("UPDATE account SET online = 0 WHERE id = '%u'", GetAccountId());
324
325        ///- If the player is in a guild, update the guild roster and broadcast a logout message to other guild members
326        Guild *guild = objmgr.GetGuildById(_player->GetGuildId());
327        if(guild)
328        {
329            guild->LoadPlayerStatsByGuid(_player->GetGUID());
330            guild->UpdateLogoutTime(_player->GetGUID());
331
332            WorldPacket data(SMSG_GUILD_EVENT, (1+1+12+8)); // name limited to 12 in character table.
333            data<<(uint8)GE_SIGNED_OFF;
334            data<<(uint8)1;
335            data<<_player->GetName();
336            data<<_player->GetGUID();
337            guild->BroadcastPacket(&data);
338        }
339
340        ///- Remove pet
341        _player->RemovePet(NULL,PET_SAVE_AS_CURRENT, true);
342
343        ///- empty buyback items and save the player in the database
344        // some save parts only correctly work in case player present in map/player_lists (pets, etc)
345        if(Save)
346        {
347            uint32 eslot;
348            for(int j = BUYBACK_SLOT_START; j < BUYBACK_SLOT_END; j++)
349            {
350                eslot = j - BUYBACK_SLOT_START;
351                _player->SetUInt64Value(PLAYER_FIELD_VENDORBUYBACK_SLOT_1+eslot*2,0);
352                _player->SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1+eslot,0);
353                _player->SetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1+eslot,0);
354            }
355            _player->SaveToDB();
356        }
357
358        ///- Leave all channels before player delete...
359        _player->CleanupChannels();
360
361        ///- If the player is in a group (or invited), remove him. If the group if then only 1 person, disband the group.
362        _player->UninviteFromGroup();
363
364        // remove player from the group if he is:
365        // a) in group; b) not in raid group; c) logging out normally (not being kicked or disconnected)
366        if(_player->GetGroup() && !_player->GetGroup()->isRaidGroup() && m_Socket)
367            _player->RemoveFromGroup();
368
369        // Unpossess the current possessed unit of player
370        if (_player->isPossessing())
371            _player->RemovePossess(false);
372
373        // Remove any possession of this player on logout
374        _player->UnpossessSelf(false);
375
376        ///- Remove the player from the world
377        // the player may not be in the world when logging out
378        // e.g if he got disconnected during a transfer to another map
379        // calls to GetMap in this case may cause crashes
380        if(_player->IsInWorld()) MapManager::Instance().GetMap(_player->GetMapId(), _player)->Remove(_player, false);
381        // RemoveFromWorld does cleanup that requires the player to be in the accessor
382        ObjectAccessor::Instance().RemoveObject(_player);
383
384        ///- Send update to group
385        if(_player->GetGroup())
386            _player->GetGroup()->SendUpdate();
387
388        ///- Broadcast a logout message to the player's friends
389        sSocialMgr.SendFriendStatus(_player, FRIEND_OFFLINE, _player->GetGUIDLow(), true);
390
391        ///- Delete the player object
392        _player->CleanupsBeforeDelete();                    // do some cleanup before deleting to prevent crash at crossreferences to already deleted data
393
394        sSocialMgr.RemovePlayerSocial (_player->GetGUIDLow ());
395        delete _player;
396        _player = NULL;
397
398        ///- Send the 'logout complete' packet to the client
399        WorldPacket data( SMSG_LOGOUT_COMPLETE, 0 );
400        SendPacket( &data );
401
402        ///- Since each account can only have one online character at any given time, ensure all characters for active account are marked as offline
403        //No SQL injection as AccountId is uint32
404        CharacterDatabase.PExecute("UPDATE characters SET online = 0 WHERE account = '%u'",
405                        GetAccountId());
406        sLog.outDebug( "SESSION: Sent SMSG_LOGOUT_COMPLETE Message" );
407    }
408
409    m_playerLogout = false;
410    m_playerRecentlyLogout = true;
411    LogoutRequest(0);
412}
413
414/// Kick a player out of the World
415void WorldSession::KickPlayer()
416{
417        if (m_Socket)
418                m_Socket->CloseSocket ();
419}
420
421/// Cancel channeling handler
422
423void WorldSession::SendAreaTriggerMessage(const char* Text, ...)
424{
425    va_list ap;
426    char szStr [1024];
427    szStr[0] = '\0';
428
429    va_start(ap, Text);
430    vsnprintf( szStr, 1024, Text, ap );
431    va_end(ap);
432
433    uint32 length = strlen(szStr)+1;
434    WorldPacket data(SMSG_AREA_TRIGGER_MESSAGE, 4+length);
435    data << length;
436    data << szStr;
437    SendPacket(&data);
438}
439
440void WorldSession::SendNotification(const char *format,...)
441{
442    if(format)
443    {
444        va_list ap;
445        char szStr [1024];
446        szStr[0] = '\0';
447        va_start(ap, format);
448        vsnprintf( szStr, 1024, format, ap );
449        va_end(ap);
450
451        WorldPacket data(SMSG_NOTIFICATION, (strlen(szStr)+1));
452        data << szStr;
453        SendPacket(&data);
454    }
455}
456
457void WorldSession::SendNotification(int32 string_id,...)
458{
459    char const* format = GetTrinityString(string_id);
460    if(format)
461    {
462        va_list ap;
463        char szStr [1024];
464        szStr[0] = '\0';
465        va_start(ap, string_id);
466        vsnprintf( szStr, 1024, format, ap );
467        va_end(ap);
468
469        WorldPacket data(SMSG_NOTIFICATION, (strlen(szStr)+1));
470        data << szStr;
471        SendPacket(&data);
472    }
473}
474
475const char * WorldSession::GetTrinityString( int32 entry ) const
476{
477    return objmgr.GetTrinityString(entry,GetSessionDbLocaleIndex());
478}
479
480void WorldSession::Handle_NULL( WorldPacket& recvPacket )
481{
482    sLog.outError( "SESSION: received unhandled opcode %s (0x%.4X)",
483        LookupOpcodeName(recvPacket.GetOpcode()),
484        recvPacket.GetOpcode());
485}
486
487void WorldSession::Handle_EarlyProccess( WorldPacket& recvPacket )
488{
489    sLog.outError( "SESSION: received opcode %s (0x%.4X) that must be proccessed in WorldSocket::OnRead",
490        LookupOpcodeName(recvPacket.GetOpcode()),
491        recvPacket.GetOpcode());
492}
493
494void WorldSession::Handle_ServerSide( WorldPacket& recvPacket )
495{
496    sLog.outError( "SESSION: received sever-side opcode %s (0x%.4X)",
497        LookupOpcodeName(recvPacket.GetOpcode()),
498        recvPacket.GetOpcode());
499}
500
501void WorldSession::Handle_Depricated( WorldPacket& recvPacket )
502{
503    sLog.outError( "SESSION: received depricated opcode %s (0x%.4X)",
504        LookupOpcodeName(recvPacket.GetOpcode()),
505        recvPacket.GetOpcode());
506}
507
508void WorldSession::SendAuthWaitQue(uint32 position)
509 {
510     if(position == 0)
511     {
512         WorldPacket packet( SMSG_AUTH_RESPONSE, 1 );
513         packet << uint8( AUTH_OK );
514         SendPacket(&packet);
515     }
516     else
517     {
518         WorldPacket packet( SMSG_AUTH_RESPONSE, 5 );
519         packet << uint8( AUTH_WAIT_QUEUE );
520         packet << uint32 (position);
521         SendPacket(&packet);
522     }
523 }
524 
525
526
527
Note: See TracBrowser for help on using the browser.