Merge branch 'master' into 303
[getmangos.git] / src / game / MiscHandler.cpp
blob29bb37bc1078c20bf4e633675720a0dbf427afd3
1 /*
2 * Copyright (C) 2005-2008 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 #include "Common.h"
20 #include "Language.h"
21 #include "Database/DatabaseEnv.h"
22 #include "Database/DatabaseImpl.h"
23 #include "WorldPacket.h"
24 #include "Opcodes.h"
25 #include "Log.h"
26 #include "Player.h"
27 #include "World.h"
28 #include "ObjectMgr.h"
29 #include "WorldSession.h"
30 #include "Auth/BigNumber.h"
31 #include "Auth/Sha1.h"
32 #include "UpdateData.h"
33 #include "LootMgr.h"
34 #include "Chat.h"
35 #include "ScriptCalls.h"
36 #include <zlib/zlib.h>
37 #include "MapManager.h"
38 #include "ObjectAccessor.h"
39 #include "Object.h"
40 #include "BattleGround.h"
41 #include "SpellAuras.h"
42 #include "Pet.h"
43 #include "SocialMgr.h"
44 #include "Tools.h"
46 void WorldSession::HandleRepopRequestOpcode( WorldPacket & /*recv_data*/ )
48 sLog.outDebug( "WORLD: Recvd CMSG_REPOP_REQUEST Message" );
50 if(GetPlayer()->isAlive()||GetPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
51 return;
53 // the world update order is sessions, players, creatures
54 // the netcode runs in parallel with all of these
55 // creatures can kill players
56 // so if the server is lagging enough the player can
57 // release spirit after he's killed but before he is updated
58 if(GetPlayer()->getDeathState() == JUST_DIED)
60 sLog.outDebug("HandleRepopRequestOpcode: got request after player %s(%d) was killed and before he was updated", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow());
61 GetPlayer()->KillPlayer();
64 //this is spirit release confirm?
65 GetPlayer()->RemovePet(NULL,PET_SAVE_NOT_IN_SLOT, true);
66 GetPlayer()->BuildPlayerRepop();
67 GetPlayer()->RepopAtGraveyard();
70 void WorldSession::HandleWhoOpcode( WorldPacket & recv_data )
72 CHECK_PACKET_SIZE(recv_data,4+4+1+1+4+4+4+4);
74 sLog.outDebug( "WORLD: Recvd CMSG_WHO Message" );
75 //recv_data.hexlike();
77 uint32 clientcount = 0;
79 uint32 level_min, level_max, racemask, classmask, zones_count, str_count;
80 uint32 zoneids[10]; // 10 is client limit
81 std::string player_name, guild_name;
83 recv_data >> level_min; // maximal player level, default 0
84 recv_data >> level_max; // minimal player level, default 100
85 recv_data >> player_name; // player name, case sensitive...
87 // recheck
88 CHECK_PACKET_SIZE(recv_data,4+4+(player_name.size()+1)+1+4+4+4+4);
90 recv_data >> guild_name; // guild name, case sensitive...
92 // recheck
93 CHECK_PACKET_SIZE(recv_data,4+4+(player_name.size()+1)+(guild_name.size()+1)+4+4+4+4);
95 recv_data >> racemask; // race mask
96 recv_data >> classmask; // class mask
97 recv_data >> zones_count; // zones count, client limit=10 (2.0.10)
99 if(zones_count > 10)
100 return; // can't be received from real client or broken packet
102 // recheck
103 CHECK_PACKET_SIZE(recv_data,4+4+(player_name.size()+1)+(guild_name.size()+1)+4+4+4+(4*zones_count)+4);
105 for(uint32 i = 0; i < zones_count; i++)
107 uint32 temp;
108 recv_data >> temp; // zone id, 0 if zone is unknown...
109 zoneids[i] = temp;
110 sLog.outDebug("Zone %u: %u", i, zoneids[i]);
113 recv_data >> str_count; // user entered strings count, client limit=4 (checked on 2.0.10)
115 if(str_count > 4)
116 return; // can't be received from real client or broken packet
118 // recheck
119 CHECK_PACKET_SIZE(recv_data,4+4+(player_name.size()+1)+(guild_name.size()+1)+4+4+4+(4*zones_count)+4+(1*str_count));
121 sLog.outDebug("Minlvl %u, maxlvl %u, name %s, guild %s, racemask %u, classmask %u, zones %u, strings %u", level_min, level_max, player_name.c_str(), guild_name.c_str(), racemask, classmask, zones_count, str_count);
123 std::wstring str[4]; // 4 is client limit
124 for(uint32 i = 0; i < str_count; i++)
126 // recheck (have one more byte)
127 CHECK_PACKET_SIZE(recv_data,recv_data.rpos());
129 std::string temp;
130 recv_data >> temp; // user entered string, it used as universal search pattern(guild+player name)?
132 if(!Utf8toWStr(temp,str[i]))
133 continue;
135 wstrToLower(str[i]);
137 sLog.outDebug("String %u: %s", i, temp.c_str());
140 std::wstring wplayer_name;
141 std::wstring wguild_name;
142 if(!(Utf8toWStr(player_name, wplayer_name) && Utf8toWStr(guild_name, wguild_name)))
143 return;
144 wstrToLower(wplayer_name);
145 wstrToLower(wguild_name);
147 // client send in case not set max level value 100 but mangos support 255 max level,
148 // update it to show GMs with characters after 100 level
149 if(level_max >= 100)
150 level_max = 255;
152 uint32 team = _player->GetTeam();
153 uint32 security = GetSecurity();
154 bool allowTwoSideWhoList = sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_WHO_LIST);
155 bool gmInWhoList = sWorld.getConfig(CONFIG_GM_IN_WHO_LIST);
157 WorldPacket data( SMSG_WHO, 50 ); // guess size
158 data << clientcount; // clientcount place holder
159 data << clientcount; // clientcount place holder
161 //TODO: Guard Player map
162 HashMapHolder<Player>::MapType& m = ObjectAccessor::Instance().GetPlayers();
163 for(HashMapHolder<Player>::MapType::iterator itr = m.begin(); itr != m.end(); ++itr)
165 if (security == SEC_PLAYER)
167 // player can see member of other team only if CONFIG_ALLOW_TWO_SIDE_WHO_LIST
168 if (itr->second->GetTeam() != team && !allowTwoSideWhoList )
169 continue;
171 // player can see MODERATOR, GAME MASTER, ADMINISTRATOR only if CONFIG_GM_IN_WHO_LIST
172 if ((itr->second->GetSession()->GetSecurity() > SEC_PLAYER && !gmInWhoList))
173 continue;
176 // check if target is globally visible for player
177 if (!(itr->second->IsVisibleGloballyFor(_player)))
178 continue;
180 // check if target's level is in level range
181 uint32 lvl = itr->second->getLevel();
182 if (lvl < level_min || lvl > level_max)
183 continue;
185 // check if class matches classmask
186 uint32 class_ = itr->second->getClass();
187 if (!(classmask & (1 << class_)))
188 continue;
190 // check if race matches racemask
191 uint32 race = itr->second->getRace();
192 if (!(racemask & (1 << race)))
193 continue;
195 uint32 pzoneid = itr->second->GetZoneId();
197 bool z_show = true;
198 for(uint32 i = 0; i < zones_count; i++)
200 if(zoneids[i] == pzoneid)
202 z_show = true;
203 break;
206 z_show = false;
208 if (!z_show)
209 continue;
211 std::string pname = itr->second->GetName();
212 std::wstring wpname;
213 if(!Utf8toWStr(pname,wpname))
214 continue;
215 wstrToLower(wpname);
217 if (!(wplayer_name.empty() || wpname.find(wplayer_name) != std::wstring::npos))
218 continue;
220 std::string gname = objmgr.GetGuildNameById(itr->second->GetGuildId());
221 std::wstring wgname;
222 if(!Utf8toWStr(gname,wgname))
223 continue;
224 wstrToLower(wgname);
226 if (!(wguild_name.empty() || wgname.find(wguild_name) != std::wstring::npos))
227 continue;
229 std::string aname;
230 if(AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(itr->second->GetZoneId()))
231 aname = areaEntry->area_name[GetSessionDbcLocale()];
233 bool s_show = true;
234 for(uint32 i = 0; i < str_count; i++)
236 if (!str[i].empty())
238 if (wgname.find(str[i]) != std::wstring::npos ||
239 wpname.find(str[i]) != std::wstring::npos ||
240 Utf8FitTo(aname, str[i]) )
242 s_show = true;
243 break;
245 s_show = false;
248 if (!s_show)
249 continue;
251 data << pname; // player name
252 data << gname; // guild name
253 data << uint32( lvl ); // player level
254 data << uint32( class_ ); // player class
255 data << uint32( race ); // player race
256 data << uint8(0); // new 2.4.0
257 data << uint32( pzoneid ); // player zone id
259 // 49 is maximum player count sent to client
260 if ((++clientcount) == 49)
261 break;
264 data.put( 0, clientcount ); //insert right count
265 data.put( sizeof(uint32), clientcount ); //insert right count
267 SendPacket(&data);
268 sLog.outDebug( "WORLD: Send SMSG_WHO Message" );
271 void WorldSession::HandleLogoutRequestOpcode( WorldPacket & /*recv_data*/ )
273 sLog.outDebug( "WORLD: Recvd CMSG_LOGOUT_REQUEST Message, security - %u", GetSecurity() );
275 if (uint64 lguid = GetPlayer()->GetLootGUID())
276 DoLootRelease(lguid);
278 //instant logout for admins, gm's, mod's
279 if( GetSecurity() > SEC_PLAYER )
281 LogoutPlayer(true);
282 return;
285 //Can not logout if...
286 if( GetPlayer()->isInCombat() || //...is in combat
287 GetPlayer()->duel || //...is in Duel
288 //...is jumping ...is falling
289 GetPlayer()->HasUnitMovementFlag(MOVEMENTFLAG_JUMPING | MOVEMENTFLAG_FALLING))
291 WorldPacket data( SMSG_LOGOUT_RESPONSE, (2+4) ) ;
292 data << (uint8)0xC;
293 data << uint32(0);
294 data << uint8(0);
295 SendPacket( &data );
296 LogoutRequest(0);
297 return;
300 //instant logout in taverns/cities or on taxi
301 if(GetPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) || GetPlayer()->isInFlight())
303 LogoutPlayer(true);
304 return;
307 // not set flags if player can't free move to prevent lost state at logout cancel
308 if(GetPlayer()->CanFreeMove())
310 GetPlayer()->SetStandState(PLAYER_STATE_SIT);
312 WorldPacket data( SMSG_FORCE_MOVE_ROOT, (8+4) ); // guess size
313 data.append(GetPlayer()->GetPackGUID());
314 data << (uint32)2;
315 SendPacket( &data );
316 GetPlayer()->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED);
319 WorldPacket data( SMSG_LOGOUT_RESPONSE, 5 );
320 data << uint32(0);
321 data << uint8(0);
322 SendPacket( &data );
323 LogoutRequest(time(NULL));
326 void WorldSession::HandlePlayerLogoutOpcode( WorldPacket & /*recv_data*/ )
328 sLog.outDebug( "WORLD: Recvd CMSG_PLAYER_LOGOUT Message" );
331 void WorldSession::HandleLogoutCancelOpcode( WorldPacket & /*recv_data*/ )
333 sLog.outDebug( "WORLD: Recvd CMSG_LOGOUT_CANCEL Message" );
335 LogoutRequest(0);
337 WorldPacket data( SMSG_LOGOUT_CANCEL_ACK, 0 );
338 SendPacket( &data );
340 // not remove flags if can't free move - its not set in Logout request code.
341 if(GetPlayer()->CanFreeMove())
343 //!we can move again
344 data.Initialize( SMSG_FORCE_MOVE_UNROOT, 8 ); // guess size
345 data.append(GetPlayer()->GetPackGUID());
346 data << uint32(0);
347 SendPacket( &data );
349 //! Stand Up
350 GetPlayer()->SetStandState(PLAYER_STATE_NONE);
352 //! DISABLE_ROTATE
353 GetPlayer()->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED);
356 sLog.outDebug( "WORLD: sent SMSG_LOGOUT_CANCEL_ACK Message" );
359 void WorldSession::SendGMTicketGetTicket(uint32 status, char const* text)
361 int len = text ? strlen(text) : 0;
362 WorldPacket data( SMSG_GMTICKET_GETTICKET, (4+len+1+4+2+4+4) );
363 data << uint32(status); // standard 0x0A, 0x06 if text present
364 if(status == 6)
366 data << text; // ticket text
367 data << uint8(0x7); // ticket category
368 data << float(0); // time from ticket creation?
369 data << float(0); // const
370 data << float(0); // const
371 data << uint8(0); // const
372 data << uint8(0); // const
374 SendPacket( &data );
377 void WorldSession::HandleGMTicketGetTicketOpcode( WorldPacket & /*recv_data*/ )
379 WorldPacket data( SMSG_QUERY_TIME_RESPONSE, 4+4 );
380 data << (uint32)time(NULL);
381 data << (uint32)0;
382 SendPacket( &data );
384 uint64 guid;
385 Field *fields;
386 guid = GetPlayer()->GetGUID();
388 QueryResult *result = CharacterDatabase.PQuery("SELECT COUNT(ticket_id) FROM character_ticket WHERE guid = '%u'", GUID_LOPART(guid));
390 if (result)
392 int cnt;
393 fields = result->Fetch();
394 cnt = fields[0].GetUInt32();
395 delete result;
397 if ( cnt > 0 )
399 QueryResult *result2 = CharacterDatabase.PQuery("SELECT ticket_text FROM character_ticket WHERE guid = '%u'", GUID_LOPART(guid));
400 if(result2)
402 Field *fields2 = result2->Fetch();
403 SendGMTicketGetTicket(0x06,fields2[0].GetString());
404 delete result2;
407 else
408 SendGMTicketGetTicket(0x0A,0);
412 void WorldSession::HandleGMTicketUpdateTextOpcode( WorldPacket & recv_data )
414 CHECK_PACKET_SIZE(recv_data,1);
416 std::string ticketText;
417 recv_data >> ticketText;
419 CharacterDatabase.escape_string(ticketText);
420 CharacterDatabase.PExecute("UPDATE character_ticket SET ticket_text = '%s' WHERE guid = '%u'", ticketText.c_str(), _player->GetGUIDLow());
423 void WorldSession::HandleGMTicketDeleteOpcode( WorldPacket & /*recv_data*/ )
425 uint32 guid = GetPlayer()->GetGUIDLow();
427 CharacterDatabase.PExecute("DELETE FROM character_ticket WHERE guid = '%u' LIMIT 1",guid);
429 WorldPacket data( SMSG_GMTICKET_DELETETICKET, 4 );
430 data << uint32(9);
431 SendPacket( &data );
433 SendGMTicketGetTicket(0x0A, 0);
436 void WorldSession::HandleGMTicketCreateOpcode( WorldPacket & recv_data )
438 CHECK_PACKET_SIZE(recv_data, 4*4+1+2*4);
440 uint32 map;
441 float x, y, z;
442 std::string ticketText = "";
443 uint32 unk1, unk2;
445 recv_data >> map >> x >> y >> z; // last check 2.4.3
446 recv_data >> ticketText;
448 // recheck
449 CHECK_PACKET_SIZE(recv_data,4*4+(ticketText.size()+1)+2*4);
451 recv_data >> unk1 >> unk2;
452 // note: the packet might contain more data, but the exact structure of that is unknown
454 sLog.outDebug("TicketCreate: map %u, x %f, y %f, z %f, text %s, unk1 %u, unk2 %u", map, x, y, z, ticketText.c_str(), unk1, unk2);
456 CharacterDatabase.escape_string(ticketText);
458 QueryResult *result = CharacterDatabase.PQuery("SELECT COUNT(*) FROM character_ticket WHERE guid = '%u'", _player->GetGUIDLow());
460 if (result)
462 int cnt;
463 Field *fields = result->Fetch();
464 cnt = fields[0].GetUInt32();
465 delete result;
467 if ( cnt > 0 )
469 WorldPacket data( SMSG_GMTICKET_CREATE, 4 );
470 data << uint32(1);
471 SendPacket( &data );
473 else
475 CharacterDatabase.PExecute("INSERT INTO character_ticket (guid,ticket_text) VALUES ('%u', '%s')", _player->GetGUIDLow(), ticketText.c_str());
477 WorldPacket data( SMSG_QUERY_TIME_RESPONSE, 4+4 );
478 data << (uint32)time(NULL);
479 data << (uint32)0;
480 SendPacket( &data );
482 data.Initialize( SMSG_GMTICKET_CREATE, 4 );
483 data << uint32(2);
484 SendPacket( &data );
485 DEBUG_LOG("update the ticket\n");
487 //TODO: Guard player map
488 HashMapHolder<Player>::MapType &m = ObjectAccessor::Instance().GetPlayers();
489 for(HashMapHolder<Player>::MapType::iterator itr = m.begin(); itr != m.end(); ++itr)
491 if(itr->second->GetSession()->GetSecurity() >= SEC_GAMEMASTER && itr->second->isAcceptTickets())
492 ChatHandler(itr->second).PSendSysMessage(LANG_COMMAND_TICKETNEW,GetPlayer()->GetName());
498 void WorldSession::HandleGMTicketSystemStatusOpcode( WorldPacket & /*recv_data*/ )
500 WorldPacket data( SMSG_GMTICKET_SYSTEMSTATUS,4 );
501 data << uint32(1); // we can also disable ticket system by sending 0 value
503 SendPacket( &data );
506 void WorldSession::HandleGMSurveySubmit( WorldPacket & recv_data)
508 // GM survey is shown after SMSG_GM_TICKET_STATUS_UPDATE with status = 3
509 CHECK_PACKET_SIZE(recv_data,4+4);
510 uint32 x;
511 recv_data >> x; // answer range? (6 = 0-5?)
512 sLog.outDebug("SURVEY: X = %u", x);
514 uint8 result[10];
515 memset(result, 0, sizeof(result));
516 for( int i = 0; i < 10; ++i)
518 CHECK_PACKET_SIZE(recv_data,recv_data.rpos()+4);
519 uint32 questionID;
520 recv_data >> questionID; // GMSurveyQuestions.dbc
521 if (!questionID)
522 break;
524 CHECK_PACKET_SIZE(recv_data,recv_data.rpos()+1+1);
525 uint8 value;
526 std::string unk_text;
527 recv_data >> value; // answer
528 recv_data >> unk_text; // always empty?
530 result[i] = value;
531 sLog.outDebug("SURVEY: ID %u, value %u, text %s", questionID, value, unk_text.c_str());
534 CHECK_PACKET_SIZE(recv_data,recv_data.rpos()+1);
535 std::string comment;
536 recv_data >> comment; // addional comment
537 sLog.outDebug("SURVEY: comment %s", comment.c_str());
539 // TODO: chart this data in some way
542 void WorldSession::HandleTogglePvP( WorldPacket & recv_data )
544 // this opcode can be used in two ways: Either set explicit new status or toggle old status
545 if(recv_data.size() == 1)
547 bool newPvPStatus;
548 recv_data >> newPvPStatus;
549 GetPlayer()->ApplyModFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP, newPvPStatus);
550 GetPlayer()->ApplyModFlag(PLAYER_FLAGS, PLAYER_FLAGS_PVP, !newPvPStatus);
552 else
554 GetPlayer()->ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP);
555 GetPlayer()->ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_PVP);
558 if(GetPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP))
560 if(!GetPlayer()->IsPvP() || GetPlayer()->pvpInfo.endTimer != 0)
561 GetPlayer()->UpdatePvP(true, true);
563 else
565 if(!GetPlayer()->pvpInfo.inHostileArea && GetPlayer()->IsPvP())
566 GetPlayer()->pvpInfo.endTimer = time(NULL); // start toggle-off
570 void WorldSession::HandleZoneUpdateOpcode( WorldPacket & recv_data )
572 CHECK_PACKET_SIZE(recv_data,4);
574 uint32 newZone;
575 recv_data >> newZone;
577 sLog.outDetail("WORLD: Recvd ZONE_UPDATE: %u", newZone);
579 if(newZone != _player->GetZoneId())
580 GetPlayer()->SendInitWorldStates(); // only if really enters to new zone, not just area change, works strange...
582 GetPlayer()->UpdateZone(newZone);
585 void WorldSession::HandleSetTargetOpcode( WorldPacket & recv_data )
587 // When this packet send?
588 CHECK_PACKET_SIZE(recv_data,8);
590 uint64 guid ;
591 recv_data >> guid;
593 _player->SetUInt32Value(UNIT_FIELD_TARGET,guid);
595 // update reputation list if need
596 Unit* unit = ObjectAccessor::GetUnit(*_player, guid );
597 if(!unit)
598 return;
600 _player->SetFactionVisibleForFactionTemplateId(unit->getFaction());
603 void WorldSession::HandleSetSelectionOpcode( WorldPacket & recv_data )
605 CHECK_PACKET_SIZE(recv_data,8);
607 uint64 guid;
608 recv_data >> guid;
610 _player->SetSelection(guid);
612 // update reputation list if need
613 Unit* unit = ObjectAccessor::GetUnit(*_player, guid );
614 if(!unit)
615 return;
617 _player->SetFactionVisibleForFactionTemplateId(unit->getFaction());
620 void WorldSession::HandleStandStateChangeOpcode( WorldPacket & recv_data )
622 CHECK_PACKET_SIZE(recv_data,1);
624 sLog.outDebug( "WORLD: Received CMSG_STAND_STATE_CHANGE" );
625 uint8 animstate;
626 recv_data >> animstate;
628 _player->SetStandState(animstate);
631 void WorldSession::HandleFriendListOpcode( WorldPacket & recv_data )
633 CHECK_PACKET_SIZE(recv_data, 4);
634 sLog.outDebug( "WORLD: Received CMSG_CONTACT_LIST" );
635 uint32 unk;
636 recv_data >> unk;
637 sLog.outDebug("unk value is %u", unk);
638 _player->GetSocial()->SendSocialList();
641 void WorldSession::HandleAddFriendOpcode( WorldPacket & recv_data )
643 CHECK_PACKET_SIZE(recv_data, 1+1);
645 sLog.outDebug( "WORLD: Received CMSG_ADD_FRIEND" );
647 std::string friendName = GetMangosString(LANG_FRIEND_IGNORE_UNKNOWN);
648 std::string friendNote;
650 recv_data >> friendName;
652 // recheck
653 CHECK_PACKET_SIZE(recv_data, (friendName.size()+1)+1);
655 recv_data >> friendNote;
657 if(!normalizePlayerName(friendName))
658 return;
660 CharacterDatabase.escape_string(friendName); // prevent SQL injection - normal name don't must changed by this call
662 sLog.outDebug( "WORLD: %s asked to add friend : '%s'",
663 GetPlayer()->GetName(), friendName.c_str() );
665 CharacterDatabase.AsyncPQuery(&WorldSession::HandleAddFriendOpcodeCallBack, GetAccountId(), friendNote, "SELECT guid, race FROM characters WHERE name = '%s'", friendName.c_str());
668 void WorldSession::HandleAddFriendOpcodeCallBack(QueryResult *result, uint32 accountId, std::string friendNote)
670 if(!result)
671 return;
673 uint64 friendGuid = MAKE_NEW_GUID((*result)[0].GetUInt32(), 0, HIGHGUID_PLAYER);
674 uint32 team = Player::TeamForRace((*result)[1].GetUInt8());
676 delete result;
678 WorldSession * session = sWorld.FindSession(accountId);
679 if(!session)
680 return;
682 FriendsResult friendResult = FRIEND_NOT_FOUND;
683 if(friendGuid)
685 if(friendGuid==session->GetPlayer()->GetGUID())
686 friendResult = FRIEND_SELF;
687 else if(session->GetPlayer()->GetTeam() != team && !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_ADD_FRIEND) && session->GetSecurity() < SEC_MODERATOR)
688 friendResult = FRIEND_ENEMY;
689 else if(session->GetPlayer()->GetSocial()->HasFriend(GUID_LOPART(friendGuid)))
690 friendResult = FRIEND_ALREADY;
691 else
693 Player* pFriend = ObjectAccessor::FindPlayer(friendGuid);
694 if( pFriend && pFriend->IsInWorld() && pFriend->IsVisibleGloballyFor(session->GetPlayer()))
695 friendResult = FRIEND_ADDED_ONLINE;
696 else
697 friendResult = FRIEND_ADDED_OFFLINE;
699 if(!session->GetPlayer()->GetSocial()->AddToSocialList(GUID_LOPART(friendGuid), false))
701 friendResult = FRIEND_LIST_FULL;
702 sLog.outDebug( "WORLD: %s's friend list is full.", session->GetPlayer()->GetName());
705 session->GetPlayer()->GetSocial()->SetFriendNote(GUID_LOPART(friendGuid), friendNote);
709 sSocialMgr.SendFriendStatus(session->GetPlayer(), friendResult, GUID_LOPART(friendGuid), false);
711 sLog.outDebug( "WORLD: Sent (SMSG_FRIEND_STATUS)" );
714 void WorldSession::HandleDelFriendOpcode( WorldPacket & recv_data )
716 CHECK_PACKET_SIZE(recv_data, 8);
718 uint64 FriendGUID;
720 sLog.outDebug( "WORLD: Received CMSG_DEL_FRIEND" );
722 recv_data >> FriendGUID;
724 _player->GetSocial()->RemoveFromSocialList(GUID_LOPART(FriendGUID), false);
726 sSocialMgr.SendFriendStatus(GetPlayer(), FRIEND_REMOVED, GUID_LOPART(FriendGUID), false);
728 sLog.outDebug( "WORLD: Sent motd (SMSG_FRIEND_STATUS)" );
731 void WorldSession::HandleAddIgnoreOpcode( WorldPacket & recv_data )
733 CHECK_PACKET_SIZE(recv_data,1);
735 sLog.outDebug( "WORLD: Received CMSG_ADD_IGNORE" );
737 std::string IgnoreName = GetMangosString(LANG_FRIEND_IGNORE_UNKNOWN);
739 recv_data >> IgnoreName;
741 if(!normalizePlayerName(IgnoreName))
742 return;
744 CharacterDatabase.escape_string(IgnoreName); // prevent SQL injection - normal name don't must changed by this call
746 sLog.outDebug( "WORLD: %s asked to Ignore: '%s'",
747 GetPlayer()->GetName(), IgnoreName.c_str() );
749 CharacterDatabase.AsyncPQuery(&WorldSession::HandleAddIgnoreOpcodeCallBack, GetAccountId(), "SELECT guid FROM characters WHERE name = '%s'", IgnoreName.c_str());
752 void WorldSession::HandleAddIgnoreOpcodeCallBack(QueryResult *result, uint32 accountId)
754 if(!result)
755 return;
757 uint64 IgnoreGuid = MAKE_NEW_GUID((*result)[0].GetUInt32(), 0, HIGHGUID_PLAYER);
759 delete result;
761 WorldSession * session = sWorld.FindSession(accountId);
762 if(!session)
763 return;
765 FriendsResult ignoreResult = FRIEND_IGNORE_NOT_FOUND;
766 if(IgnoreGuid)
768 if(IgnoreGuid==session->GetPlayer()->GetGUID()) //not add yourself
769 ignoreResult = FRIEND_IGNORE_SELF;
770 else if( session->GetPlayer()->GetSocial()->HasIgnore(GUID_LOPART(IgnoreGuid)) )
771 ignoreResult = FRIEND_IGNORE_ALREADY;
772 else
774 ignoreResult = FRIEND_IGNORE_ADDED;
776 // ignore list full
777 if(!session->GetPlayer()->GetSocial()->AddToSocialList(GUID_LOPART(IgnoreGuid), true))
778 ignoreResult = FRIEND_IGNORE_FULL;
782 sSocialMgr.SendFriendStatus(session->GetPlayer(), ignoreResult, GUID_LOPART(IgnoreGuid), false);
784 sLog.outDebug( "WORLD: Sent (SMSG_FRIEND_STATUS)" );
787 void WorldSession::HandleDelIgnoreOpcode( WorldPacket & recv_data )
789 CHECK_PACKET_SIZE(recv_data, 8);
791 uint64 IgnoreGUID;
793 sLog.outDebug( "WORLD: Received CMSG_DEL_IGNORE" );
795 recv_data >> IgnoreGUID;
797 _player->GetSocial()->RemoveFromSocialList(GUID_LOPART(IgnoreGUID), true);
799 sSocialMgr.SendFriendStatus(GetPlayer(), FRIEND_IGNORE_REMOVED, GUID_LOPART(IgnoreGUID), false);
801 sLog.outDebug( "WORLD: Sent motd (SMSG_FRIEND_STATUS)" );
804 void WorldSession::HandleSetFriendNoteOpcode( WorldPacket & recv_data )
806 CHECK_PACKET_SIZE(recv_data, 8+1);
807 uint64 guid;
808 std::string note;
809 recv_data >> guid >> note;
810 _player->GetSocial()->SetFriendNote(guid, note);
813 void WorldSession::HandleBugOpcode( WorldPacket & recv_data )
815 CHECK_PACKET_SIZE(recv_data,4+4+1+4+1);
817 uint32 suggestion, contentlen;
818 std::string content;
819 uint32 typelen;
820 std::string type;
822 recv_data >> suggestion >> contentlen >> content;
824 //recheck
825 CHECK_PACKET_SIZE(recv_data,4+4+(content.size()+1)+4+1);
827 recv_data >> typelen >> type;
829 if( suggestion == 0 )
830 sLog.outDebug( "WORLD: Received CMSG_BUG [Bug Report]" );
831 else
832 sLog.outDebug( "WORLD: Received CMSG_BUG [Suggestion]" );
834 sLog.outDebug( type.c_str( ) );
835 sLog.outDebug( content.c_str( ) );
837 CharacterDatabase.escape_string(type);
838 CharacterDatabase.escape_string(content);
839 CharacterDatabase.PExecute ("INSERT INTO bugreport (type,content) VALUES('%s', '%s')", type.c_str( ), content.c_str( ));
842 void WorldSession::HandleCorpseReclaimOpcode(WorldPacket &recv_data)
844 CHECK_PACKET_SIZE(recv_data,8);
846 sLog.outDetail("WORLD: Received CMSG_RECLAIM_CORPSE");
847 if (GetPlayer()->isAlive())
848 return;
850 // body not released yet
851 if(!GetPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
852 return;
854 Corpse *corpse = GetPlayer()->GetCorpse();
856 if (!corpse )
857 return;
859 // prevent resurrect before 30-sec delay after body release not finished
860 if(corpse->GetGhostTime() + GetPlayer()->GetCorpseReclaimDelay(corpse->GetType()==CORPSE_RESURRECTABLE_PVP) > time(NULL))
861 return;
863 float dist = corpse->GetDistance2d(GetPlayer());
864 sLog.outDebug("Corpse 2D Distance: \t%f",dist);
865 if (dist > CORPSE_RECLAIM_RADIUS)
866 return;
868 uint64 guid;
869 recv_data >> guid;
871 // resurrect
872 GetPlayer()->ResurrectPlayer(GetPlayer()->InBattleGround() ? 1.0f : 0.5f);
874 // spawn bones
875 GetPlayer()->SpawnCorpseBones();
877 GetPlayer()->SaveToDB();
880 void WorldSession::HandleResurrectResponseOpcode(WorldPacket & recv_data)
882 CHECK_PACKET_SIZE(recv_data,8+1);
884 sLog.outDetail("WORLD: Received CMSG_RESURRECT_RESPONSE");
886 if(GetPlayer()->isAlive())
887 return;
889 uint64 guid;
890 uint8 status;
891 recv_data >> guid;
892 recv_data >> status;
894 if(status == 0)
896 GetPlayer()->clearResurrectRequestData(); // reject
897 return;
900 if(!GetPlayer()->isRessurectRequestedBy(guid))
901 return;
903 GetPlayer()->ResurectUsingRequestData();
904 GetPlayer()->SaveToDB();
907 void WorldSession::HandleAreaTriggerOpcode(WorldPacket & recv_data)
909 CHECK_PACKET_SIZE(recv_data,4);
911 sLog.outDebug("WORLD: Received CMSG_AREATRIGGER");
913 uint32 Trigger_ID;
915 recv_data >> Trigger_ID;
916 sLog.outDebug("Trigger ID:%u",Trigger_ID);
918 if(GetPlayer()->isInFlight())
920 sLog.outDebug("Player '%s' (GUID: %u) in flight, ignore Area Trigger ID:%u",GetPlayer()->GetName(),GetPlayer()->GetGUIDLow(), Trigger_ID);
921 return;
924 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
925 if(!atEntry)
927 sLog.outDebug("Player '%s' (GUID: %u) send unknown (by DBC) Area Trigger ID:%u",GetPlayer()->GetName(),GetPlayer()->GetGUIDLow(), Trigger_ID);
928 return;
931 if (GetPlayer()->GetMapId()!=atEntry->mapid)
933 sLog.outDebug("Player '%s' (GUID: %u) too far (trigger map: %u player map: %u), ignore Area Trigger ID: %u", GetPlayer()->GetName(), atEntry->mapid, GetPlayer()->GetMapId(), GetPlayer()->GetGUIDLow(), Trigger_ID);
934 return;
937 // delta is safe radius
938 const float delta = 5.0f;
939 // check if player in the range of areatrigger
940 Player* pl = GetPlayer();
942 if (atEntry->radius > 0)
944 // if we have radius check it
945 float dist = pl->GetDistance(atEntry->x,atEntry->y,atEntry->z);
946 if(dist > atEntry->radius + delta)
948 sLog.outDebug("Player '%s' (GUID: %u) too far (radius: %f distance: %f), ignore Area Trigger ID: %u",
949 pl->GetName(), pl->GetGUIDLow(), atEntry->radius, dist, Trigger_ID);
950 return;
953 else
955 // we have only extent
956 float dx = pl->GetPositionX() - atEntry->x;
957 float dy = pl->GetPositionY() - atEntry->y;
958 float dz = pl->GetPositionZ() - atEntry->z;
959 double es = sin(atEntry->box_orientation);
960 double ec = cos(atEntry->box_orientation);
961 // calc rotated vector based on extent axis
962 double rotateDx = dx*ec - dy*es;
963 double rotateDy = dx*es + dy*ec;
965 if( (fabs(rotateDx) > atEntry->box_x/2 + delta) ||
966 (fabs(rotateDy) > atEntry->box_y/2 + delta) ||
967 (fabs(dz) > atEntry->box_z/2 + delta) )
969 sLog.outDebug("Player '%s' (GUID: %u) too far (1/2 box X: %f 1/2 box Y: %f 1/2 box Z: %f rotate dX: %f rotate dY: %f dZ:%f), ignore Area Trigger ID: %u",
970 pl->GetName(), pl->GetGUIDLow(), atEntry->box_x/2, atEntry->box_y/2, atEntry->box_z/2, rotateDx, rotateDy, dz, Trigger_ID);
971 return;
975 if(Script->scriptAreaTrigger(GetPlayer(), atEntry))
976 return;
978 uint32 quest_id = objmgr.GetQuestForAreaTrigger( Trigger_ID );
979 if( quest_id && GetPlayer()->isAlive() && GetPlayer()->IsActiveQuest(quest_id) )
981 Quest const* pQuest = objmgr.GetQuestTemplate(quest_id);
982 if( pQuest )
984 if(GetPlayer()->GetQuestStatus(quest_id) == QUEST_STATUS_INCOMPLETE)
985 GetPlayer()->AreaExploredOrEventHappens( quest_id );
989 if(objmgr.IsTavernAreaTrigger(Trigger_ID))
991 // set resting flag we are in the inn
992 GetPlayer()->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING);
993 GetPlayer()->InnEnter(time(NULL), atEntry->mapid, atEntry->x, atEntry->y, atEntry->z);
994 GetPlayer()->SetRestType(REST_TYPE_IN_TAVERN);
996 if(sWorld.IsFFAPvPRealm())
997 GetPlayer()->RemoveFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP);
999 return;
1002 if(GetPlayer()->InBattleGround())
1004 BattleGround* bg = GetPlayer()->GetBattleGround();
1005 if(bg)
1006 if(bg->GetStatus() == STATUS_IN_PROGRESS)
1007 bg->HandleAreaTrigger(GetPlayer(), Trigger_ID);
1009 return;
1012 // NULL if all values default (non teleport trigger)
1013 AreaTrigger const* at = objmgr.GetAreaTrigger(Trigger_ID);
1014 if(!at)
1015 return;
1017 if(!GetPlayer()->isGameMaster())
1019 uint32 missingLevel = 0;
1020 if(GetPlayer()->getLevel() < at->requiredLevel && !sWorld.getConfig(CONFIG_INSTANCE_IGNORE_LEVEL))
1021 missingLevel = at->requiredLevel;
1023 // must have one or the other, report the first one that's missing
1024 uint32 missingItem = 0;
1025 if(at->requiredItem)
1027 if(!GetPlayer()->HasItemCount(at->requiredItem, 1) &&
1028 (!at->requiredItem2 || !GetPlayer()->HasItemCount(at->requiredItem2, 1)))
1029 missingItem = at->requiredItem;
1031 else if(at->requiredItem2 && !GetPlayer()->HasItemCount(at->requiredItem2, 1))
1032 missingItem = at->requiredItem2;
1034 uint32 missingKey = 0;
1035 if(GetPlayer()->GetDifficulty() == DIFFICULTY_HEROIC)
1037 if(at->heroicKey)
1039 if(!GetPlayer()->HasItemCount(at->heroicKey, 1) &&
1040 (!at->heroicKey2 || !GetPlayer()->HasItemCount(at->heroicKey2, 1)))
1041 missingKey = at->heroicKey;
1043 else if(at->heroicKey2 && !GetPlayer()->HasItemCount(at->heroicKey2, 1))
1044 missingKey = at->heroicKey2;
1047 uint32 missingQuest = 0;
1048 if(at->requiredQuest && !GetPlayer()->GetQuestRewardStatus(at->requiredQuest))
1049 missingQuest = at->requiredQuest;
1051 if(missingLevel || missingItem || missingKey || missingQuest)
1053 // TODO: all this is probably wrong
1054 if(missingItem)
1055 SendAreaTriggerMessage(GetMangosString(LANG_LEVEL_MINREQUIRED_AND_ITEM), at->requiredLevel, objmgr.GetItemPrototype(missingItem)->Name1);
1056 else if(missingKey)
1057 GetPlayer()->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_DIFFICULTY, DIFFICULTY_HEROIC);
1058 else if(missingQuest)
1059 SendAreaTriggerMessage(at->requiredFailedText.c_str());
1060 else if(missingLevel)
1061 SendAreaTriggerMessage(GetMangosString(LANG_LEVEL_MINREQUIRED), missingLevel);
1062 return;
1066 GetPlayer()->TeleportTo(at->target_mapId,at->target_X,at->target_Y,at->target_Z,at->target_Orientation,TELE_TO_NOT_LEAVE_TRANSPORT);
1069 void WorldSession::HandleUpdateAccountData(WorldPacket &recv_data)
1071 sLog.outDetail("WORLD: Received CMSG_UPDATE_ACCOUNT_DATA");
1073 CHECK_PACKET_SIZE(recv_data, 4+4+4);
1075 uint32 type, timestamp, decompressedSize;
1076 recv_data >> type >> timestamp >> decompressedSize;
1078 if(type > NUM_ACCOUNT_DATA_TYPES)
1079 return;
1081 if(decompressedSize == 0) // erase
1083 SetAccountData(type, timestamp, "");
1085 WorldPacket data(SMSG_UPDATE_ACCOUNT_DATA_COMPLETE, 4+4);
1086 data << uint32(type);
1087 data << uint32(0);
1088 SendPacket(&data);
1090 return;
1093 if(decompressedSize > 0xFFFF)
1095 sLog.outError("UAD: Account data packet too big, size %u", decompressedSize);
1096 return;
1099 ByteBuffer dest;
1100 dest.resize(decompressedSize);
1102 uLongf realSize = decompressedSize;
1103 if(uncompress(const_cast<uint8*>(dest.contents()), &realSize, const_cast<uint8*>(recv_data.contents() + recv_data.rpos()), recv_data.size() - recv_data.rpos()) != Z_OK)
1105 sLog.outError("UAD: Failed to decompress account data");
1106 return;
1109 std::string adata;
1110 dest >> adata;
1112 SetAccountData(type, timestamp, adata);
1114 WorldPacket data(SMSG_UPDATE_ACCOUNT_DATA_COMPLETE, 4+4);
1115 data << uint32(type);
1116 data << uint32(0);
1117 SendPacket(&data);
1120 void WorldSession::HandleRequestAccountData(WorldPacket& recv_data)
1122 sLog.outDetail("WORLD: Received CMSG_REQUEST_ACCOUNT_DATA");
1124 CHECK_PACKET_SIZE(recv_data, 4);
1126 uint32 type;
1127 recv_data >> type;
1129 if(type > NUM_ACCOUNT_DATA_TYPES)
1130 return;
1132 AccountData *adata = GetAccountData(type);
1134 uint32 size = adata->Data.size();
1136 ByteBuffer dest;
1137 dest.resize(size);
1139 uLongf destSize = size;
1140 if(compress(const_cast<uint8*>(dest.contents()), &destSize, (uint8*)adata->Data.c_str(), size) != Z_OK)
1142 sLog.outDebug("RAD: Failed to compress account data");
1143 return;
1146 dest.resize(destSize);
1148 WorldPacket data (SMSG_UPDATE_ACCOUNT_DATA, 8+4+4+4+destSize);
1149 data << uint64(_player->GetGUID()); // player guid
1150 data << uint32(type); // type (0-7)
1151 data << uint32(adata->Time); // unix time
1152 data << uint32(size); // decompressed length
1153 data.append(dest); // compressed data
1154 SendPacket(&data);
1157 void WorldSession::HandleSetActionButtonOpcode(WorldPacket& recv_data)
1159 CHECK_PACKET_SIZE(recv_data,1+2+1+1);
1161 sLog.outDebug( "WORLD: Received CMSG_SET_ACTION_BUTTON" );
1162 uint8 button, misc, type;
1163 uint16 action;
1164 recv_data >> button >> action >> misc >> type;
1165 sLog.outDetail( "BUTTON: %u ACTION: %u TYPE: %u MISC: %u", button, action, type, misc );
1166 if(action==0)
1168 sLog.outDetail( "MISC: Remove action from button %u", button );
1170 GetPlayer()->removeActionButton(button);
1172 else
1174 if(type==ACTION_BUTTON_MACRO || type==ACTION_BUTTON_CMACRO)
1176 sLog.outDetail( "MISC: Added Macro %u into button %u", action, button );
1177 GetPlayer()->addActionButton(button,action,type,misc);
1179 else if(type==ACTION_BUTTON_SPELL)
1181 sLog.outDetail( "MISC: Added Action %u into button %u", action, button );
1182 GetPlayer()->addActionButton(button,action,type,misc);
1184 else if(type==ACTION_BUTTON_ITEM)
1186 sLog.outDetail( "MISC: Added Item %u into button %u", action, button );
1187 GetPlayer()->addActionButton(button,action,type,misc);
1189 else
1190 sLog.outError( "MISC: Unknown action button type %u for action %u into button %u", type, action, button );
1194 void WorldSession::HandleCompleteCinema( WorldPacket & /*recv_data*/ )
1196 DEBUG_LOG( "WORLD: Player is watching cinema" );
1199 void WorldSession::HandleNextCinematicCamera( WorldPacket & /*recv_data*/ )
1201 DEBUG_LOG( "WORLD: Which movie to play" );
1204 void WorldSession::HandleMoveTimeSkippedOpcode( WorldPacket & /*recv_data*/ )
1206 /* WorldSession::Update( getMSTime() );*/
1207 DEBUG_LOG( "WORLD: Time Lag/Synchronization Resent/Update" );
1210 CHECK_PACKET_SIZE(recv_data,8+4);
1211 uint64 guid;
1212 uint32 time_skipped;
1213 recv_data >> guid;
1214 recv_data >> time_skipped;
1215 sLog.outDebug( "WORLD: CMSG_MOVE_TIME_SKIPPED" );
1217 /// TODO
1218 must be need use in mangos
1219 We substract server Lags to move time ( AntiLags )
1220 for exmaple
1221 GetPlayer()->ModifyLastMoveTime( -int32(time_skipped) );
1225 void WorldSession::HandleFeatherFallAck(WorldPacket &/*recv_data*/)
1227 DEBUG_LOG("WORLD: CMSG_MOVE_FEATHER_FALL_ACK");
1230 void WorldSession::HandleMoveUnRootAck(WorldPacket&/* recv_data*/)
1233 CHECK_PACKET_SIZE(recv_data,8+8+4+4+4+4+4);
1235 sLog.outDebug( "WORLD: CMSG_FORCE_MOVE_UNROOT_ACK" );
1236 recv_data.hexlike();
1237 uint64 guid;
1238 uint64 unknown1;
1239 uint32 unknown2;
1240 float PositionX;
1241 float PositionY;
1242 float PositionZ;
1243 float Orientation;
1245 recv_data >> guid;
1246 recv_data >> unknown1;
1247 recv_data >> unknown2;
1248 recv_data >> PositionX;
1249 recv_data >> PositionY;
1250 recv_data >> PositionZ;
1251 recv_data >> Orientation;
1253 // TODO for later may be we can use for anticheat
1254 DEBUG_LOG("Guid " I64FMTD,guid);
1255 DEBUG_LOG("unknown1 " I64FMTD,unknown1);
1256 DEBUG_LOG("unknown2 %u",unknown2);
1257 DEBUG_LOG("X %f",PositionX);
1258 DEBUG_LOG("Y %f",PositionY);
1259 DEBUG_LOG("Z %f",PositionZ);
1260 DEBUG_LOG("O %f",Orientation);
1264 void WorldSession::HandleMoveRootAck(WorldPacket&/* recv_data*/)
1267 CHECK_PACKET_SIZE(recv_data,8+8+4+4+4+4+4);
1269 sLog.outDebug( "WORLD: CMSG_FORCE_MOVE_ROOT_ACK" );
1270 recv_data.hexlike();
1271 uint64 guid;
1272 uint64 unknown1;
1273 uint32 unknown2;
1274 float PositionX;
1275 float PositionY;
1276 float PositionZ;
1277 float Orientation;
1279 recv_data >> guid;
1280 recv_data >> unknown1;
1281 recv_data >> unknown2;
1282 recv_data >> PositionX;
1283 recv_data >> PositionY;
1284 recv_data >> PositionZ;
1285 recv_data >> Orientation;
1287 // for later may be we can use for anticheat
1288 DEBUG_LOG("Guid " I64FMTD,guid);
1289 DEBUG_LOG("unknown1 " I64FMTD,unknown1);
1290 DEBUG_LOG("unknown1 %u",unknown2);
1291 DEBUG_LOG("X %f",PositionX);
1292 DEBUG_LOG("Y %f",PositionY);
1293 DEBUG_LOG("Z %f",PositionZ);
1294 DEBUG_LOG("O %f",Orientation);
1298 void WorldSession::HandleMoveTeleportAck(WorldPacket&/* recv_data*/)
1301 CHECK_PACKET_SIZE(recv_data,8+4);
1303 sLog.outDebug("MSG_MOVE_TELEPORT_ACK");
1304 uint64 guid;
1305 uint32 flags, time;
1307 recv_data >> guid;
1308 recv_data >> flags >> time;
1309 DEBUG_LOG("Guid " I64FMTD,guid);
1310 DEBUG_LOG("Flags %u, time %u",flags, time/1000);
1314 void WorldSession::HandleSetActionBar(WorldPacket& recv_data)
1316 CHECK_PACKET_SIZE(recv_data,1);
1318 uint8 ActionBar;
1320 recv_data >> ActionBar;
1322 if(!GetPlayer()) // ignore until not logged (check needed because STATUS_AUTHED)
1324 if(ActionBar!=0)
1325 sLog.outError("WorldSession::HandleSetActionBar in not logged state with value: %u, ignored",uint32(ActionBar));
1326 return;
1329 GetPlayer()->SetByteValue(PLAYER_FIELD_BYTES, 2, ActionBar);
1332 void WorldSession::HandleWardenDataOpcode(WorldPacket& /*recv_data*/)
1335 CHECK_PACKET_SIZE(recv_data,1);
1337 uint8 tmp;
1338 recv_data >> tmp;
1339 sLog.outDebug("Received opcode CMSG_WARDEN_DATA, not resolve.uint8 = %u",tmp);
1343 void WorldSession::HandlePlayedTime(WorldPacket& /*recv_data*/)
1345 uint32 TotalTimePlayed = GetPlayer()->GetTotalPlayedTime();
1346 uint32 LevelPlayedTime = GetPlayer()->GetLevelPlayedTime();
1348 WorldPacket data(SMSG_PLAYED_TIME, 8);
1349 data << TotalTimePlayed;
1350 data << LevelPlayedTime;
1351 SendPacket(&data);
1354 void WorldSession::HandleInspectOpcode(WorldPacket& recv_data)
1356 CHECK_PACKET_SIZE(recv_data, 8);
1358 uint64 guid;
1359 recv_data >> guid;
1360 DEBUG_LOG("Inspected guid is " I64FMTD, guid);
1362 _player->SetSelection(guid);
1364 Player *plr = objmgr.GetPlayer(guid);
1365 if(!plr) // wrong player
1366 return;
1368 uint32 talent_points = 0x3D;
1369 uint32 guid_size = plr->GetPackGUID().size();
1370 WorldPacket data(SMSG_INSPECT_TALENT, 4+talent_points);
1371 data.append(plr->GetPackGUID());
1372 data << uint32(talent_points);
1374 // fill by 0 talents array
1375 for(uint32 i = 0; i < talent_points; ++i)
1376 data << uint8(0);
1378 if(sWorld.getConfig(CONFIG_TALENTS_INSPECTING) || _player->isGameMaster())
1380 // find class talent tabs (all players have 3 talent tabs)
1381 uint32 const* talentTabIds = GetTalentTabPages(plr->getClass());
1383 uint32 talentTabPos = 0; // pos of first talent rank in tab including all prev tabs
1384 for(uint32 i = 0; i < 3; ++i)
1386 uint32 talentTabId = talentTabIds[i];
1388 // fill by real data
1389 for(uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
1391 TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
1392 if(!talentInfo)
1393 continue;
1395 // skip another tab talents
1396 if(talentInfo->TalentTab != talentTabId)
1397 continue;
1399 // find talent rank
1400 uint32 curtalent_maxrank = 0;
1401 for(uint32 k = 5; k > 0; --k)
1403 if(talentInfo->RankID[k-1] && plr->HasSpell(talentInfo->RankID[k-1]))
1405 curtalent_maxrank = k;
1406 break;
1410 // not learned talent
1411 if(!curtalent_maxrank)
1412 continue;
1414 // 1 rank talent bit index
1415 uint32 curtalent_index = talentTabPos + GetTalentInspectBitPosInTab(talentId);
1417 uint32 curtalent_rank_index = curtalent_index+curtalent_maxrank-1;
1419 // slot/offset in 7-bit bytes
1420 uint32 curtalent_rank_slot7 = curtalent_rank_index / 7;
1421 uint32 curtalent_rank_offset7 = curtalent_rank_index % 7;
1423 // rank pos with skipped 8 bit
1424 uint32 curtalent_rank_index2 = curtalent_rank_slot7 * 8 + curtalent_rank_offset7;
1426 // slot/offset in 8-bit bytes with skipped high bit
1427 uint32 curtalent_rank_slot = curtalent_rank_index2 / 8;
1428 uint32 curtalent_rank_offset = curtalent_rank_index2 % 8;
1430 // apply mask
1431 uint32 val = data.read<uint8>(guid_size + 4 + curtalent_rank_slot);
1432 val |= (1 << curtalent_rank_offset);
1433 data.put<uint8>(guid_size + 4 + curtalent_rank_slot, val & 0xFF);
1436 talentTabPos += GetTalentTabInspectBitSize(talentTabId);
1440 SendPacket(&data);
1443 void WorldSession::HandleInspectHonorStatsOpcode(WorldPacket& recv_data)
1445 CHECK_PACKET_SIZE(recv_data, 8);
1447 uint64 guid;
1448 recv_data >> guid;
1450 Player *player = objmgr.GetPlayer(guid);
1452 if(!player)
1454 sLog.outError("InspectHonorStats: WTF, player not found...");
1455 return;
1458 WorldPacket data(MSG_INSPECT_HONOR_STATS, 8+1+4*4);
1459 data << uint64(player->GetGUID());
1460 data << uint8(player->GetUInt32Value(PLAYER_FIELD_HONOR_CURRENCY));
1461 data << uint32(player->GetUInt32Value(PLAYER_FIELD_KILLS));
1462 data << uint32(player->GetUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION));
1463 data << uint32(player->GetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION));
1464 data << uint32(player->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS));
1465 SendPacket(&data);
1468 void WorldSession::HandleWorldTeleportOpcode(WorldPacket& recv_data)
1470 CHECK_PACKET_SIZE(recv_data,4+4+4+4+4+4);
1472 // write in client console: worldport 469 452 6454 2536 180 or /console worldport 469 452 6454 2536 180
1473 // Received opcode CMSG_WORLD_TELEPORT
1474 // Time is ***, map=469, x=452.000000, y=6454.000000, z=2536.000000, orient=3.141593
1476 //sLog.outDebug("Received opcode CMSG_WORLD_TELEPORT");
1478 if(GetPlayer()->isInFlight())
1480 sLog.outDebug("Player '%s' (GUID: %u) in flight, ignore worldport command.",GetPlayer()->GetName(),GetPlayer()->GetGUIDLow());
1481 return;
1484 uint32 time;
1485 uint32 mapid;
1486 float PositionX;
1487 float PositionY;
1488 float PositionZ;
1489 float Orientation;
1491 recv_data >> time; // time in m.sec.
1492 recv_data >> mapid;
1493 recv_data >> PositionX;
1494 recv_data >> PositionY;
1495 recv_data >> PositionZ;
1496 recv_data >> Orientation; // o (3.141593 = 180 degrees)
1497 DEBUG_LOG("Time %u sec, map=%u, x=%f, y=%f, z=%f, orient=%f", time/1000, mapid, PositionX, PositionY, PositionZ, Orientation);
1499 if (GetSecurity() >= SEC_ADMINISTRATOR)
1500 GetPlayer()->TeleportTo(mapid,PositionX,PositionY,PositionZ,Orientation);
1501 else
1502 SendNotification(LANG_YOU_NOT_HAVE_PERMISSION);
1503 sLog.outDebug("Received worldport command from player %s", GetPlayer()->GetName());
1506 void WorldSession::HandleWhoisOpcode(WorldPacket& recv_data)
1508 CHECK_PACKET_SIZE(recv_data, 1);
1510 sLog.outDebug("Received opcode CMSG_WHOIS");
1511 std::string charname;
1512 recv_data >> charname;
1514 if (GetSecurity() < SEC_ADMINISTRATOR)
1516 SendNotification(LANG_YOU_NOT_HAVE_PERMISSION);
1517 return;
1520 if(charname.empty())
1522 SendNotification(LANG_NEED_CHARACTER_NAME);
1523 return;
1526 Player *plr = objmgr.GetPlayer(charname.c_str());
1528 if(!plr)
1530 SendNotification(LANG_PLAYER_NOT_EXIST_OR_OFFLINE, charname.c_str());
1531 return;
1534 uint32 accid = plr->GetSession()->GetAccountId();
1536 QueryResult *result = loginDatabase.PQuery("SELECT username,email,last_ip FROM account WHERE id=%u", accid);
1537 if(!result)
1539 SendNotification(LANG_ACCOUNT_FOR_PLAYER_NOT_FOUND, charname.c_str());
1540 return;
1543 Field *fields = result->Fetch();
1544 std::string acc = fields[0].GetCppString();
1545 if(acc.empty())
1546 acc = "Unknown";
1547 std::string email = fields[1].GetCppString();
1548 if(email.empty())
1549 email = "Unknown";
1550 std::string lastip = fields[2].GetCppString();
1551 if(lastip.empty())
1552 lastip = "Unknown";
1554 std::string msg = charname + "'s " + "account is " + acc + ", e-mail: " + email + ", last ip: " + lastip;
1556 WorldPacket data(SMSG_WHOIS, msg.size()+1);
1557 data << msg;
1558 _player->GetSession()->SendPacket(&data);
1560 delete result;
1562 sLog.outDebug("Received whois command from player %s for character %s", GetPlayer()->GetName(), charname.c_str());
1565 void WorldSession::HandleReportSpamOpcode( WorldPacket & recv_data )
1567 CHECK_PACKET_SIZE(recv_data, 1+8);
1568 sLog.outDebug("WORLD: CMSG_REPORT_SPAM");
1569 recv_data.hexlike();
1571 uint8 spam_type; // 0 - mail, 1 - chat
1572 uint64 spammer_guid;
1573 uint32 unk1, unk2, unk3, unk4 = 0;
1574 std::string description = "";
1575 recv_data >> spam_type; // unk 0x01 const, may be spam type (mail/chat)
1576 recv_data >> spammer_guid; // player guid
1577 switch(spam_type)
1579 case 0:
1580 CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4+4+4);
1581 recv_data >> unk1; // const 0
1582 recv_data >> unk2; // probably mail id
1583 recv_data >> unk3; // const 0
1584 break;
1585 case 1:
1586 CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4+4+4+4+1);
1587 recv_data >> unk1; // probably language
1588 recv_data >> unk2; // message type?
1589 recv_data >> unk3; // probably channel id
1590 recv_data >> unk4; // unk random value
1591 recv_data >> description; // spam description string (messagetype, channel name, player name, message)
1592 break;
1595 // NOTE: all chat messages from this spammer automatically ignored by spam reporter until logout in case chat spam.
1596 // if it's mail spam - ALL mails from this spammer automatically removed by client
1598 // Complaint Received message
1599 WorldPacket data(SMSG_COMPLAIN_RESULT, 1);
1600 data << uint8(0);
1601 SendPacket(&data);
1603 sLog.outDebug("REPORT SPAM: type %u, guid %u, unk1 %u, unk2 %u, unk3 %u, unk4 %u, message %s", spam_type, GUID_LOPART(spammer_guid), unk1, unk2, unk3, unk4, description.c_str());
1606 void WorldSession::HandleRealmStateRequestOpcode( WorldPacket & recv_data )
1608 CHECK_PACKET_SIZE(recv_data, 4);
1610 sLog.outDebug("CMSG_REALM_SPLIT");
1612 uint32 unk;
1613 std::string split_date = "01/01/01";
1614 recv_data >> unk;
1616 WorldPacket data(SMSG_REALM_SPLIT, 4+4+split_date.size()+1);
1617 data << unk;
1618 data << uint32(0x00000000); // realm split state
1619 // split states:
1620 // 0x0 realm normal
1621 // 0x1 realm split
1622 // 0x2 realm split pending
1623 data << split_date;
1624 SendPacket(&data);
1625 //sLog.outDebug("response sent %u", unk);
1628 void WorldSession::HandleFarSightOpcode( WorldPacket & recv_data )
1630 CHECK_PACKET_SIZE(recv_data, 1);
1632 sLog.outDebug("WORLD: CMSG_FAR_SIGHT");
1633 //recv_data.hexlike();
1635 uint8 unk;
1636 recv_data >> unk;
1638 switch(unk)
1640 case 0:
1641 //WorldPacket data(SMSG_CLEAR_FAR_SIGHT_IMMEDIATE, 0)
1642 //SendPacket(&data);
1643 //_player->SetUInt64Value(PLAYER_FARSIGHT, 0);
1644 sLog.outDebug("Removed FarSight from player %u", _player->GetGUIDLow());
1645 break;
1646 case 1:
1647 sLog.outDebug("Added FarSight " I64FMTD " to player %u", _player->GetUInt64Value(PLAYER_FARSIGHT), _player->GetGUIDLow());
1648 break;
1652 void WorldSession::HandleChooseTitleOpcode( WorldPacket & recv_data )
1654 CHECK_PACKET_SIZE(recv_data, 4);
1656 sLog.outDebug("CMSG_SET_TITLE");
1658 int32 title;
1659 recv_data >> title;
1661 // -1 at none
1662 if(title > 0 && title < 64)
1664 if(!GetPlayer()->HasFlag64(PLAYER__FIELD_KNOWN_TITLES,uint64(1) << title))
1665 return;
1667 else
1668 title = 0;
1670 GetPlayer()->SetUInt32Value(PLAYER_CHOSEN_TITLE, title);
1673 void WorldSession::HandleAllowMoveAckOpcode( WorldPacket & recv_data )
1675 CHECK_PACKET_SIZE(recv_data, 4+4);
1677 sLog.outDebug("CMSG_ALLOW_MOVE_ACK");
1679 uint32 counter, time_;
1680 recv_data >> counter >> time_;
1682 // time_ seems always more than getMSTime()
1683 uint32 diff = getMSTimeDiff(getMSTime(),time_);
1685 sLog.outDebug("response sent: counter %u, time %u (HEX: %X), ms. time %u, diff %u", counter, time_, time_, getMSTime(), diff);
1688 void WorldSession::HandleResetInstancesOpcode( WorldPacket & /*recv_data*/ )
1690 sLog.outDebug("WORLD: CMSG_RESET_INSTANCES");
1691 Group *pGroup = _player->GetGroup();
1692 if(pGroup)
1694 if(pGroup->IsLeader(_player->GetGUID()))
1695 pGroup->ResetInstances(INSTANCE_RESET_ALL, _player);
1697 else
1698 _player->ResetInstances(INSTANCE_RESET_ALL);
1701 void WorldSession::HandleDungeonDifficultyOpcode( WorldPacket & recv_data )
1703 CHECK_PACKET_SIZE(recv_data, 4);
1705 sLog.outDebug("MSG_SET_DUNGEON_DIFFICULTY");
1707 uint32 mode;
1708 recv_data >> mode;
1710 if(mode == _player->GetDifficulty())
1711 return;
1713 if(mode > DIFFICULTY_HEROIC)
1715 sLog.outError("WorldSession::HandleDungeonDifficultyOpcode: player %d sent an invalid instance mode %d!", _player->GetGUIDLow(), mode);
1716 return;
1719 // cannot reset while in an instance
1720 Map *map = _player->GetMap();
1721 if(map && map->IsDungeon())
1723 sLog.outError("WorldSession::HandleDungeonDifficultyOpcode: player %d tried to reset the instance while inside!", _player->GetGUIDLow());
1724 return;
1727 if(_player->getLevel() < LEVELREQUIREMENT_HEROIC)
1728 return;
1729 Group *pGroup = _player->GetGroup();
1730 if(pGroup)
1732 if(pGroup->IsLeader(_player->GetGUID()))
1734 // the difficulty is set even if the instances can't be reset
1735 //_player->SendDungeonDifficulty(true);
1736 pGroup->ResetInstances(INSTANCE_RESET_CHANGE_DIFFICULTY, _player);
1737 pGroup->SetDifficulty(mode);
1740 else
1742 _player->ResetInstances(INSTANCE_RESET_CHANGE_DIFFICULTY);
1743 _player->SetDifficulty(mode);
1747 void WorldSession::HandleNewUnknownOpcode( WorldPacket & recv_data )
1749 sLog.outDebug("New Unknown Opcode %u", recv_data.GetOpcode());
1750 recv_data.hexlike();
1752 New Unknown Opcode 837
1753 STORAGE_SIZE: 60
1754 02 00 00 00 00 00 00 00 | 00 00 00 00 01 20 00 00
1755 89 EB 33 01 71 5C 24 C4 | 15 03 35 45 74 47 8B 42
1756 BA B8 1B 40 00 00 00 00 | 00 00 00 00 77 66 42 BF
1757 23 91 26 3F 00 00 60 41 | 00 00 00 00
1759 New Unknown Opcode 837
1760 STORAGE_SIZE: 44
1761 02 00 00 00 00 00 00 00 | 00 00 00 00 00 00 80 00
1762 7B 80 34 01 84 EA 2B C4 | 5F A1 36 45 C9 39 1C 42
1763 BA B8 1B 40 CE 06 00 00 | 00 00 80 3F
1767 void WorldSession::HandleDismountOpcode( WorldPacket & /*recv_data*/ )
1769 sLog.outDebug("WORLD: CMSG_CANCEL_MOUNT_AURA");
1770 //recv_data.hexlike();
1772 //If player is not mounted, so go out :)
1773 if (!_player->IsMounted()) // not blizz like; no any messages on blizz
1775 ChatHandler(this).SendSysMessage(LANG_CHAR_NON_MOUNTED);
1776 return;
1779 if(_player->isInFlight()) // not blizz like; no any messages on blizz
1781 ChatHandler(this).SendSysMessage(LANG_YOU_IN_FLIGHT);
1782 return;
1785 _player->Unmount();
1786 _player->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED);
1789 void WorldSession::HandleMoveFlyModeChangeAckOpcode( WorldPacket & recv_data )
1791 CHECK_PACKET_SIZE(recv_data, 8+4+4);
1793 // fly mode on/off
1794 sLog.outDebug("WORLD: CMSG_MOVE_SET_CAN_FLY_ACK");
1795 //recv_data.hexlike();
1797 uint64 guid;
1798 uint32 unk;
1799 uint32 flags;
1801 recv_data >> guid >> unk >> flags;
1803 _player->SetUnitMovementFlags(flags);
1806 25 00 00 00 00 00 00 00 | 00 00 00 00 00 00 80 00
1807 85 4E A9 01 19 BA 7A C3 | 42 0D 70 44 44 B0 A8 42
1808 78 15 94 40 39 03 00 00 | 00 00 80 3F
1809 off:
1810 25 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00
1811 10 FD A9 01 19 BA 7A C3 | 42 0D 70 44 44 B0 A8 42
1812 78 15 94 40 39 03 00 00 | 00 00 00 00
1816 void WorldSession::HandleRequestPetInfoOpcode( WorldPacket & /*recv_data */)
1819 sLog.outDebug("WORLD: CMSG_REQUEST_PET_INFO");
1820 recv_data.hexlike();
1824 void WorldSession::HandleSetTaxiBenchmarkOpcode( WorldPacket & recv_data )
1826 CHECK_PACKET_SIZE(recv_data, 1);
1828 uint8 mode;
1829 recv_data >> mode;
1831 sLog.outDebug("Client used \"/timetest %d\" command", mode);
1834 void WorldSession::HandleSpellClick( WorldPacket & recv_data )
1836 CHECK_PACKET_SIZE(recv_data, 8);
1838 uint64 guid;
1839 recv_data >> guid;
1841 Unit *vehicle = ObjectAccessor::GetUnit(*_player, guid);
1843 if(!vehicle)
1844 return;
1846 _player->SetClientControl(vehicle, 1);
1847 _player->CastSpell(_player, 43768, true);
1848 _player->SetUInt64Value(UNIT_FIELD_CHARM, guid);
1849 _player->SetUInt64Value(PLAYER_FARSIGHT, guid);
1852 void WorldSession::HandleInspectAchievements( WorldPacket & recv_data )
1854 sLog.outString("WorldSession::HandleInspectAchievements");
1855 uint64 guid;
1856 if(!readGUID(recv_data, guid))
1857 return;
1859 Player *player = objmgr.GetPlayer(guid);
1860 if(!player)
1861 return;
1863 player->GetAchievementMgr().SendRespondInspectAchievements(_player);