mirror of
https://github.com/araxiaonline/TrinityCore.git
synced 2026-06-20 06:59:46 -04:00
2ea31027e5
Make acessible all the info about current realm (e.g name) anywhere, not only realm id Reduce the number of differences between the two branches Original changes by Shauren Partial port ofbacc90b6baand63def8aa32
1828 lines
61 KiB
C++
1828 lines
61 KiB
C++
/*
|
|
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
|
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "Common.h"
|
|
#include "Language.h"
|
|
#include "DatabaseEnv.h"
|
|
#include "WorldPacket.h"
|
|
#include "Opcodes.h"
|
|
#include "Log.h"
|
|
#include "Player.h"
|
|
#include "GossipDef.h"
|
|
#include "World.h"
|
|
#include "ObjectMgr.h"
|
|
#include "GuildMgr.h"
|
|
#include "WorldSession.h"
|
|
#include "Chat.h"
|
|
#include "zlib.h"
|
|
#include "ObjectAccessor.h"
|
|
#include "Object.h"
|
|
#include "Battleground.h"
|
|
#include "OutdoorPvP.h"
|
|
#include "SocialMgr.h"
|
|
#include "AccountMgr.h"
|
|
#include "DBCEnums.h"
|
|
#include "ScriptMgr.h"
|
|
#include "MapManager.h"
|
|
#include "GameObjectAI.h"
|
|
#include "Group.h"
|
|
#include "Spell.h"
|
|
#include "BattlegroundMgr.h"
|
|
#include "Battlefield.h"
|
|
#include "BattlefieldMgr.h"
|
|
|
|
void WorldSession::HandleRepopRequestOpcode(WorldPacket& recvData)
|
|
{
|
|
TC_LOG_DEBUG("network", "WORLD: Recvd CMSG_REPOP_REQUEST Message");
|
|
|
|
recvData.read_skip<uint8>();
|
|
|
|
if (GetPlayer()->IsAlive() || GetPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
|
|
return;
|
|
|
|
if (GetPlayer()->HasAuraType(SPELL_AURA_PREVENT_RESURRECTION))
|
|
return; // silently return, client should display the error by itself
|
|
|
|
// the world update order is sessions, players, creatures
|
|
// the netcode runs in parallel with all of these
|
|
// creatures can kill players
|
|
// so if the server is lagging enough the player can
|
|
// release spirit after he's killed but before he is updated
|
|
if (GetPlayer()->getDeathState() == JUST_DIED)
|
|
{
|
|
TC_LOG_DEBUG("network", "HandleRepopRequestOpcode: got request after player %s(%d) was killed and before he was updated",
|
|
GetPlayer()->GetName().c_str(), GetPlayer()->GetGUID().GetCounter());
|
|
GetPlayer()->KillPlayer();
|
|
}
|
|
|
|
//this is spirit release confirm?
|
|
GetPlayer()->RemoveGhoul();
|
|
GetPlayer()->RemovePet(NULL, PET_SAVE_NOT_IN_SLOT, true);
|
|
GetPlayer()->BuildPlayerRepop();
|
|
GetPlayer()->RepopAtGraveyard();
|
|
}
|
|
|
|
void WorldSession::HandleGossipSelectOptionOpcode(WorldPacket& recvData)
|
|
{
|
|
TC_LOG_DEBUG("network", "WORLD: CMSG_GOSSIP_SELECT_OPTION");
|
|
|
|
uint32 gossipListId;
|
|
uint32 menuId;
|
|
ObjectGuid guid;
|
|
std::string code = "";
|
|
|
|
recvData >> guid >> menuId >> gossipListId;
|
|
|
|
if (!_player->PlayerTalkClass->GetGossipMenu().GetItem(gossipListId))
|
|
{
|
|
recvData.rfinish();
|
|
return;
|
|
}
|
|
|
|
if (_player->PlayerTalkClass->IsGossipOptionCoded(gossipListId))
|
|
recvData >> code;
|
|
|
|
// Prevent cheating on C++ scripted menus
|
|
if (_player->PlayerTalkClass->GetGossipMenu().GetSenderGUID() != guid)
|
|
return;
|
|
|
|
Creature* unit = NULL;
|
|
GameObject* go = NULL;
|
|
if (guid.IsCreatureOrVehicle())
|
|
{
|
|
unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_GOSSIP);
|
|
if (!unit)
|
|
{
|
|
TC_LOG_DEBUG("network", "WORLD: HandleGossipSelectOptionOpcode - %s not found or you can't interact with him.", guid.ToString().c_str());
|
|
return;
|
|
}
|
|
}
|
|
else if (guid.IsGameObject())
|
|
{
|
|
go = _player->GetGameObjectIfCanInteractWith(guid);
|
|
if (!go)
|
|
{
|
|
TC_LOG_DEBUG("network", "WORLD: HandleGossipSelectOptionOpcode - %s not found or you can't interact with it.", guid.ToString().c_str());
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TC_LOG_DEBUG("network", "WORLD: HandleGossipSelectOptionOpcode - unsupported %s.", guid.ToString().c_str());
|
|
return;
|
|
}
|
|
|
|
// remove fake death
|
|
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
|
|
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
|
|
|
if ((unit && unit->GetCreatureTemplate()->ScriptID != unit->LastUsedScriptID) || (go && go->GetGOInfo()->ScriptId != go->LastUsedScriptID))
|
|
{
|
|
TC_LOG_DEBUG("network", "WORLD: HandleGossipSelectOptionOpcode - Script reloaded while in use, ignoring and set new scipt id");
|
|
if (unit)
|
|
unit->LastUsedScriptID = unit->GetCreatureTemplate()->ScriptID;
|
|
if (go)
|
|
go->LastUsedScriptID = go->GetGOInfo()->ScriptId;
|
|
_player->PlayerTalkClass->SendCloseGossip();
|
|
return;
|
|
}
|
|
if (!code.empty())
|
|
{
|
|
if (unit)
|
|
{
|
|
unit->AI()->sGossipSelectCode(_player, menuId, gossipListId, code.c_str());
|
|
if (!sScriptMgr->OnGossipSelectCode(_player, unit, _player->PlayerTalkClass->GetGossipOptionSender(gossipListId), _player->PlayerTalkClass->GetGossipOptionAction(gossipListId), code.c_str()))
|
|
_player->OnGossipSelect(unit, gossipListId, menuId);
|
|
}
|
|
else
|
|
{
|
|
go->AI()->GossipSelectCode(_player, menuId, gossipListId, code.c_str());
|
|
if (!sScriptMgr->OnGossipSelectCode(_player, go, _player->PlayerTalkClass->GetGossipOptionSender(gossipListId), _player->PlayerTalkClass->GetGossipOptionAction(gossipListId), code.c_str()))
|
|
_player->OnGossipSelect(go, gossipListId, menuId);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (unit)
|
|
{
|
|
unit->AI()->sGossipSelect(_player, menuId, gossipListId);
|
|
if (!sScriptMgr->OnGossipSelect(_player, unit, _player->PlayerTalkClass->GetGossipOptionSender(gossipListId), _player->PlayerTalkClass->GetGossipOptionAction(gossipListId)))
|
|
_player->OnGossipSelect(unit, gossipListId, menuId);
|
|
}
|
|
else
|
|
{
|
|
go->AI()->GossipSelect(_player, menuId, gossipListId);
|
|
if (!sScriptMgr->OnGossipSelect(_player, go, _player->PlayerTalkClass->GetGossipOptionSender(gossipListId), _player->PlayerTalkClass->GetGossipOptionAction(gossipListId)))
|
|
_player->OnGossipSelect(go, gossipListId, menuId);
|
|
}
|
|
}
|
|
}
|
|
|
|
void WorldSession::HandleWhoOpcode(WorldPacket& recvData)
|
|
{
|
|
TC_LOG_DEBUG("network", "WORLD: Recvd CMSG_WHO Message");
|
|
|
|
uint32 matchcount = 0;
|
|
|
|
uint32 level_min, level_max, racemask, classmask, zones_count, str_count;
|
|
uint32 zoneids[10]; // 10 is client limit
|
|
std::string player_name, guild_name;
|
|
|
|
recvData >> level_min; // maximal player level, default 0
|
|
recvData >> level_max; // minimal player level, default 100 (MAX_LEVEL)
|
|
recvData >> player_name; // player name, case sensitive...
|
|
|
|
recvData >> guild_name; // guild name, case sensitive...
|
|
|
|
recvData >> racemask; // race mask
|
|
recvData >> classmask; // class mask
|
|
recvData >> zones_count; // zones count, client limit = 10 (2.0.10)
|
|
|
|
if (zones_count > 10)
|
|
return; // can't be received from real client or broken packet
|
|
|
|
for (uint32 i = 0; i < zones_count; ++i)
|
|
{
|
|
uint32 temp;
|
|
recvData >> temp; // zone id, 0 if zone is unknown...
|
|
zoneids[i] = temp;
|
|
TC_LOG_DEBUG("network", "Zone %u: %u", i, zoneids[i]);
|
|
}
|
|
|
|
recvData >> str_count; // user entered strings count, client limit=4 (checked on 2.0.10)
|
|
|
|
if (str_count > 4)
|
|
return; // can't be received from real client or broken packet
|
|
|
|
TC_LOG_DEBUG("network", "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);
|
|
|
|
std::wstring str[4]; // 4 is client limit
|
|
for (uint32 i = 0; i < str_count; ++i)
|
|
{
|
|
std::string temp;
|
|
recvData >> temp; // user entered string, it used as universal search pattern(guild+player name)?
|
|
|
|
if (!Utf8toWStr(temp, str[i]))
|
|
continue;
|
|
|
|
wstrToLower(str[i]);
|
|
|
|
TC_LOG_DEBUG("network", "String %u: %s", i, temp.c_str());
|
|
}
|
|
|
|
std::wstring wplayer_name;
|
|
std::wstring wguild_name;
|
|
if (!(Utf8toWStr(player_name, wplayer_name) && Utf8toWStr(guild_name, wguild_name)))
|
|
return;
|
|
wstrToLower(wplayer_name);
|
|
wstrToLower(wguild_name);
|
|
|
|
// client send in case not set max level value 100 but Trinity supports 255 max level,
|
|
// update it to show GMs with characters after 100 level
|
|
if (level_max >= MAX_LEVEL)
|
|
level_max = STRONG_MAX_LEVEL;
|
|
|
|
uint32 team = _player->GetTeam();
|
|
|
|
uint32 gmLevelInWhoList = sWorld->getIntConfig(CONFIG_GM_LEVEL_IN_WHO_LIST);
|
|
uint32 displaycount = 0;
|
|
|
|
WorldPacket data(SMSG_WHO, 50); // guess size
|
|
data << uint32(matchcount); // placeholder, count of players matching criteria
|
|
data << uint32(displaycount); // placeholder, count of players displayed
|
|
|
|
boost::shared_lock<boost::shared_mutex> lock(*HashMapHolder<Player>::GetLock());
|
|
|
|
HashMapHolder<Player>::MapType const& m = ObjectAccessor::GetPlayers();
|
|
for (HashMapHolder<Player>::MapType::const_iterator itr = m.begin(); itr != m.end(); ++itr)
|
|
{
|
|
Player* target = itr->second;
|
|
// player can see member of other team only if CONFIG_ALLOW_TWO_SIDE_WHO_LIST
|
|
if (target->GetTeam() != team && !HasPermission(rbac::RBAC_PERM_TWO_SIDE_WHO_LIST))
|
|
continue;
|
|
|
|
// player can see MODERATOR, GAME MASTER, ADMINISTRATOR only if CONFIG_GM_IN_WHO_LIST
|
|
if (!HasPermission(rbac::RBAC_PERM_WHO_SEE_ALL_SEC_LEVELS) && target->GetSession()->GetSecurity() > AccountTypes(gmLevelInWhoList))
|
|
continue;
|
|
|
|
// do not process players which are not in world
|
|
if (!target->IsInWorld())
|
|
continue;
|
|
|
|
// check if target is globally visible for player
|
|
if (!target->IsVisibleGloballyFor(_player))
|
|
continue;
|
|
|
|
// check if target's level is in level range
|
|
uint8 lvl = target->getLevel();
|
|
if (lvl < level_min || lvl > level_max)
|
|
continue;
|
|
|
|
// check if class matches classmask
|
|
uint8 class_ = target->getClass();
|
|
if (!(classmask & (1 << class_)))
|
|
continue;
|
|
|
|
// check if race matches racemask
|
|
uint32 race = target->getRace();
|
|
if (!(racemask & (1 << race)))
|
|
continue;
|
|
|
|
uint32 pzoneid = target->GetZoneId();
|
|
uint8 gender = target->GetByteValue(PLAYER_BYTES_3, 0);
|
|
|
|
bool z_show = true;
|
|
for (uint32 i = 0; i < zones_count; ++i)
|
|
{
|
|
if (zoneids[i] == pzoneid)
|
|
{
|
|
z_show = true;
|
|
break;
|
|
}
|
|
|
|
z_show = false;
|
|
}
|
|
if (!z_show)
|
|
continue;
|
|
|
|
std::string pname = target->GetName();
|
|
std::wstring wpname;
|
|
if (!Utf8toWStr(pname, wpname))
|
|
continue;
|
|
wstrToLower(wpname);
|
|
|
|
if (!(wplayer_name.empty() || wpname.find(wplayer_name) != std::wstring::npos))
|
|
continue;
|
|
|
|
std::string gname = sGuildMgr->GetGuildNameById(target->GetGuildId());
|
|
std::wstring wgname;
|
|
if (!Utf8toWStr(gname, wgname))
|
|
continue;
|
|
wstrToLower(wgname);
|
|
|
|
if (!(wguild_name.empty() || wgname.find(wguild_name) != std::wstring::npos))
|
|
continue;
|
|
|
|
std::string aname;
|
|
if (AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(pzoneid))
|
|
aname = areaEntry->area_name[GetSessionDbcLocale()];
|
|
|
|
bool s_show = true;
|
|
for (uint32 i = 0; i < str_count; ++i)
|
|
{
|
|
if (!str[i].empty())
|
|
{
|
|
if (wgname.find(str[i]) != std::wstring::npos ||
|
|
wpname.find(str[i]) != std::wstring::npos ||
|
|
Utf8FitTo(aname, str[i]))
|
|
{
|
|
s_show = true;
|
|
break;
|
|
}
|
|
s_show = false;
|
|
}
|
|
}
|
|
if (!s_show)
|
|
continue;
|
|
|
|
// 49 is maximum player count sent to client - can be overridden
|
|
// through config, but is unstable
|
|
if ((matchcount++) >= sWorld->getIntConfig(CONFIG_MAX_WHO))
|
|
continue;
|
|
|
|
data << pname; // player name
|
|
data << gname; // guild name
|
|
data << uint32(lvl); // player level
|
|
data << uint32(class_); // player class
|
|
data << uint32(race); // player race
|
|
data << uint8(gender); // player gender
|
|
data << uint32(pzoneid); // player zone id
|
|
|
|
++displaycount;
|
|
}
|
|
|
|
data.put(0, displaycount); // insert right count, count displayed
|
|
data.put(4, matchcount); // insert right count, count of matches
|
|
|
|
SendPacket(&data);
|
|
TC_LOG_DEBUG("network", "WORLD: Send SMSG_WHO Message");
|
|
}
|
|
|
|
void WorldSession::HandleLogoutRequestOpcode(WorldPacket& /*recvData*/)
|
|
{
|
|
TC_LOG_DEBUG("network", "WORLD: Recvd CMSG_LOGOUT_REQUEST Message, security - %u", GetSecurity());
|
|
|
|
if (ObjectGuid lguid = GetPlayer()->GetLootGUID())
|
|
DoLootRelease(lguid);
|
|
|
|
bool instantLogout = (GetPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) && !GetPlayer()->IsInCombat()) ||
|
|
GetPlayer()->IsInFlight() || HasPermission(rbac::RBAC_PERM_INSTANT_LOGOUT);
|
|
|
|
/// TODO: Possibly add RBAC permission to log out in combat
|
|
bool canLogoutInCombat = GetPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING);
|
|
|
|
uint32 reason = 0;
|
|
if (GetPlayer()->IsInCombat() && !canLogoutInCombat)
|
|
reason = 1;
|
|
else if (GetPlayer()->m_movementInfo.HasMovementFlag(MOVEMENTFLAG_FALLING | MOVEMENTFLAG_FALLING_FAR))
|
|
reason = 3; // is jumping or falling
|
|
else if (GetPlayer()->duel || GetPlayer()->HasAura(9454)) // is dueling or frozen by GM via freeze command
|
|
reason = 2; // FIXME - Need the correct value
|
|
|
|
WorldPacket data(SMSG_LOGOUT_RESPONSE, 1+4);
|
|
data << uint32(reason);
|
|
data << uint8(instantLogout);
|
|
SendPacket(&data);
|
|
|
|
if (reason)
|
|
{
|
|
LogoutRequest(0);
|
|
return;
|
|
}
|
|
|
|
//instant logout in taverns/cities or on taxi or for admins, gm's, mod's if its enabled in worldserver.conf
|
|
if (instantLogout)
|
|
{
|
|
LogoutPlayer(true);
|
|
return;
|
|
}
|
|
|
|
// not set flags if player can't free move to prevent lost state at logout cancel
|
|
if (GetPlayer()->CanFreeMove())
|
|
{
|
|
if (GetPlayer()->getStandState() == UNIT_STAND_STATE_STAND)
|
|
GetPlayer()->SetStandState(UNIT_STAND_STATE_SIT);
|
|
|
|
WorldPacket data(SMSG_FORCE_MOVE_ROOT, (8+4)); // guess size
|
|
data << GetPlayer()->GetPackGUID();
|
|
data << (uint32)2;
|
|
SendPacket(&data);
|
|
GetPlayer()->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED);
|
|
}
|
|
|
|
LogoutRequest(time(NULL));
|
|
}
|
|
|
|
void WorldSession::HandlePlayerLogoutOpcode(WorldPacket& /*recvData*/)
|
|
{
|
|
TC_LOG_DEBUG("network", "WORLD: Recvd CMSG_PLAYER_LOGOUT Message");
|
|
}
|
|
|
|
void WorldSession::HandleLogoutCancelOpcode(WorldPacket& /*recvData*/)
|
|
{
|
|
TC_LOG_DEBUG("network", "WORLD: Recvd CMSG_LOGOUT_CANCEL Message");
|
|
|
|
// Player have already logged out serverside, too late to cancel
|
|
if (!GetPlayer())
|
|
return;
|
|
|
|
LogoutRequest(0);
|
|
|
|
WorldPacket data(SMSG_LOGOUT_CANCEL_ACK, 0);
|
|
SendPacket(&data);
|
|
|
|
// not remove flags if can't free move - its not set in Logout request code.
|
|
if (GetPlayer()->CanFreeMove())
|
|
{
|
|
//!we can move again
|
|
data.Initialize(SMSG_FORCE_MOVE_UNROOT, 8); // guess size
|
|
data << GetPlayer()->GetPackGUID();
|
|
data << uint32(0);
|
|
SendPacket(&data);
|
|
|
|
//! Stand Up
|
|
GetPlayer()->SetStandState(UNIT_STAND_STATE_STAND);
|
|
|
|
//! DISABLE_ROTATE
|
|
GetPlayer()->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED);
|
|
}
|
|
|
|
TC_LOG_DEBUG("network", "WORLD: Sent SMSG_LOGOUT_CANCEL_ACK Message");
|
|
}
|
|
|
|
void WorldSession::HandleTogglePvP(WorldPacket& recvData)
|
|
{
|
|
// this opcode can be used in two ways: Either set explicit new status or toggle old status
|
|
if (recvData.size() == 1)
|
|
{
|
|
bool newPvPStatus;
|
|
recvData >> newPvPStatus;
|
|
GetPlayer()->ApplyModFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP, newPvPStatus);
|
|
GetPlayer()->ApplyModFlag(PLAYER_FLAGS, PLAYER_FLAGS_PVP_TIMER, !newPvPStatus);
|
|
}
|
|
else
|
|
{
|
|
GetPlayer()->ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP);
|
|
GetPlayer()->ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_PVP_TIMER);
|
|
}
|
|
|
|
if (GetPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP))
|
|
{
|
|
if (!GetPlayer()->IsPvP() || GetPlayer()->pvpInfo.EndTimer)
|
|
GetPlayer()->UpdatePvP(true, true);
|
|
}
|
|
else
|
|
{
|
|
if (!GetPlayer()->pvpInfo.IsHostile && GetPlayer()->IsPvP())
|
|
GetPlayer()->pvpInfo.EndTimer = time(NULL); // start toggle-off
|
|
}
|
|
|
|
//if (OutdoorPvP* pvp = _player->GetOutdoorPvP())
|
|
// pvp->HandlePlayerActivityChanged(_player);
|
|
}
|
|
|
|
void WorldSession::HandleZoneUpdateOpcode(WorldPacket& recvData)
|
|
{
|
|
uint32 newZone;
|
|
recvData >> newZone;
|
|
|
|
TC_LOG_DEBUG("network", "WORLD: Recvd ZONE_UPDATE: %u", newZone);
|
|
|
|
// use server side data, but only after update the player position. See Player::UpdatePosition().
|
|
GetPlayer()->SetNeedsZoneUpdate(true);
|
|
|
|
//GetPlayer()->SendInitWorldStates(true, newZone);
|
|
}
|
|
|
|
void WorldSession::HandleSetSelectionOpcode(WorldPacket& recvData)
|
|
{
|
|
ObjectGuid guid;
|
|
recvData >> guid;
|
|
|
|
_player->SetSelection(guid);
|
|
}
|
|
|
|
void WorldSession::HandleStandStateChangeOpcode(WorldPacket& recvData)
|
|
{
|
|
// TC_LOG_DEBUG("network", "WORLD: Received CMSG_STANDSTATECHANGE"); -- too many spam in log at lags/debug stop
|
|
uint32 animstate;
|
|
recvData >> animstate;
|
|
|
|
_player->SetStandState(animstate);
|
|
}
|
|
|
|
void WorldSession::HandleContactListOpcode(WorldPacket& recvData)
|
|
{
|
|
uint32 unk;
|
|
recvData >> unk;
|
|
TC_LOG_DEBUG("network", "WORLD: Received CMSG_CONTACT_LIST - Unk: %d", unk);
|
|
_player->GetSocial()->SendSocialList(_player);
|
|
}
|
|
|
|
void WorldSession::HandleAddFriendOpcode(WorldPacket& recvData)
|
|
{
|
|
TC_LOG_DEBUG("network", "WORLD: Received CMSG_ADD_FRIEND");
|
|
|
|
std::string friendName = GetTrinityString(LANG_FRIEND_IGNORE_UNKNOWN);
|
|
std::string friendNote;
|
|
|
|
recvData >> friendName;
|
|
|
|
recvData >> friendNote;
|
|
|
|
if (!normalizePlayerName(friendName))
|
|
return;
|
|
|
|
TC_LOG_DEBUG("network", "WORLD: %s asked to add friend : '%s'",
|
|
GetPlayer()->GetName().c_str(), friendName.c_str());
|
|
|
|
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GUID_RACE_ACC_BY_NAME);
|
|
|
|
stmt->setString(0, friendName);
|
|
|
|
_addFriendCallback.SetParam(friendNote);
|
|
_addFriendCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt));
|
|
}
|
|
|
|
void WorldSession::HandleAddFriendOpcodeCallBack(PreparedQueryResult result, std::string const& friendNote)
|
|
{
|
|
if (!GetPlayer())
|
|
return;
|
|
|
|
ObjectGuid friendGuid;
|
|
uint32 friendAccountId;
|
|
uint32 team;
|
|
FriendsResult friendResult;
|
|
|
|
friendResult = FRIEND_NOT_FOUND;
|
|
|
|
if (result)
|
|
{
|
|
Field* fields = result->Fetch();
|
|
|
|
friendGuid = ObjectGuid(HighGuid::Player, 0, fields[0].GetUInt32());
|
|
team = Player::TeamForRace(fields[1].GetUInt8());
|
|
friendAccountId = fields[2].GetUInt32();
|
|
|
|
if (HasPermission(rbac::RBAC_PERM_ALLOW_GM_FRIEND) || AccountMgr::IsPlayerAccount(AccountMgr::GetSecurity(friendAccountId, realm.Id.Realm)))
|
|
{
|
|
if (friendGuid)
|
|
{
|
|
if (friendGuid == GetPlayer()->GetGUID())
|
|
friendResult = FRIEND_SELF;
|
|
else if (GetPlayer()->GetTeam() != team && !HasPermission(rbac::RBAC_PERM_TWO_SIDE_ADD_FRIEND))
|
|
friendResult = FRIEND_ENEMY;
|
|
else if (GetPlayer()->GetSocial()->HasFriend(friendGuid.GetCounter()))
|
|
friendResult = FRIEND_ALREADY;
|
|
else
|
|
{
|
|
Player* pFriend = ObjectAccessor::FindPlayer(friendGuid);
|
|
if (pFriend && pFriend->IsVisibleGloballyFor(GetPlayer()))
|
|
friendResult = FRIEND_ADDED_ONLINE;
|
|
else
|
|
friendResult = FRIEND_ADDED_OFFLINE;
|
|
if (!GetPlayer()->GetSocial()->AddToSocialList(friendGuid.GetCounter(), false))
|
|
{
|
|
friendResult = FRIEND_LIST_FULL;
|
|
TC_LOG_DEBUG("network", "WORLD: %s's friend list is full.", GetPlayer()->GetName().c_str());
|
|
}
|
|
}
|
|
GetPlayer()->GetSocial()->SetFriendNote(friendGuid.GetCounter(), friendNote);
|
|
}
|
|
}
|
|
}
|
|
|
|
sSocialMgr->SendFriendStatus(GetPlayer(), friendResult, friendGuid.GetCounter(), false);
|
|
|
|
TC_LOG_DEBUG("network", "WORLD: Sent (SMSG_FRIEND_STATUS)");
|
|
}
|
|
|
|
void WorldSession::HandleDelFriendOpcode(WorldPacket& recvData)
|
|
{
|
|
ObjectGuid FriendGUID;
|
|
|
|
TC_LOG_DEBUG("network", "WORLD: Received CMSG_DEL_FRIEND");
|
|
|
|
recvData >> FriendGUID;
|
|
|
|
_player->GetSocial()->RemoveFromSocialList(FriendGUID.GetCounter(), false);
|
|
|
|
sSocialMgr->SendFriendStatus(GetPlayer(), FRIEND_REMOVED, FriendGUID.GetCounter(), false);
|
|
|
|
TC_LOG_DEBUG("network", "WORLD: Sent motd (SMSG_FRIEND_STATUS)");
|
|
}
|
|
|
|
void WorldSession::HandleAddIgnoreOpcode(WorldPacket& recvData)
|
|
{
|
|
TC_LOG_DEBUG("network", "WORLD: Received CMSG_ADD_IGNORE");
|
|
|
|
std::string ignoreName = GetTrinityString(LANG_FRIEND_IGNORE_UNKNOWN);
|
|
|
|
recvData >> ignoreName;
|
|
|
|
if (!normalizePlayerName(ignoreName))
|
|
return;
|
|
|
|
TC_LOG_DEBUG("network", "WORLD: %s asked to Ignore: '%s'",
|
|
GetPlayer()->GetName().c_str(), ignoreName.c_str());
|
|
|
|
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GUID_BY_NAME);
|
|
|
|
stmt->setString(0, ignoreName);
|
|
|
|
_addIgnoreCallback = CharacterDatabase.AsyncQuery(stmt);
|
|
}
|
|
|
|
void WorldSession::HandleAddIgnoreOpcodeCallBack(PreparedQueryResult result)
|
|
{
|
|
if (!GetPlayer())
|
|
return;
|
|
|
|
ObjectGuid IgnoreGuid;
|
|
FriendsResult ignoreResult;
|
|
|
|
ignoreResult = FRIEND_IGNORE_NOT_FOUND;
|
|
|
|
if (result)
|
|
{
|
|
IgnoreGuid = ObjectGuid(HighGuid::Player, (*result)[0].GetUInt32());
|
|
|
|
if (IgnoreGuid)
|
|
{
|
|
if (IgnoreGuid == GetPlayer()->GetGUID()) //not add yourself
|
|
ignoreResult = FRIEND_IGNORE_SELF;
|
|
else if (GetPlayer()->GetSocial()->HasIgnore(IgnoreGuid.GetCounter()))
|
|
ignoreResult = FRIEND_IGNORE_ALREADY;
|
|
else
|
|
{
|
|
ignoreResult = FRIEND_IGNORE_ADDED;
|
|
|
|
// ignore list full
|
|
if (!GetPlayer()->GetSocial()->AddToSocialList(IgnoreGuid.GetCounter(), true))
|
|
ignoreResult = FRIEND_IGNORE_FULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
sSocialMgr->SendFriendStatus(GetPlayer(), ignoreResult, IgnoreGuid.GetCounter(), false);
|
|
|
|
TC_LOG_DEBUG("network", "WORLD: Sent (SMSG_FRIEND_STATUS)");
|
|
}
|
|
|
|
void WorldSession::HandleDelIgnoreOpcode(WorldPacket& recvData)
|
|
{
|
|
ObjectGuid IgnoreGUID;
|
|
|
|
TC_LOG_DEBUG("network", "WORLD: Received CMSG_DEL_IGNORE");
|
|
|
|
recvData >> IgnoreGUID;
|
|
|
|
_player->GetSocial()->RemoveFromSocialList(IgnoreGUID.GetCounter(), true);
|
|
|
|
sSocialMgr->SendFriendStatus(GetPlayer(), FRIEND_IGNORE_REMOVED, IgnoreGUID.GetCounter(), false);
|
|
|
|
TC_LOG_DEBUG("network", "WORLD: Sent motd (SMSG_FRIEND_STATUS)");
|
|
}
|
|
|
|
void WorldSession::HandleSetContactNotesOpcode(WorldPacket& recvData)
|
|
{
|
|
TC_LOG_DEBUG("network", "CMSG_SET_CONTACT_NOTES");
|
|
ObjectGuid guid;
|
|
std::string note;
|
|
recvData >> guid >> note;
|
|
_player->GetSocial()->SetFriendNote(guid.GetCounter(), note);
|
|
}
|
|
|
|
void WorldSession::HandleBugOpcode(WorldPacket& recvData)
|
|
{
|
|
uint32 suggestion, contentlen, typelen;
|
|
std::string content, type;
|
|
|
|
recvData >> suggestion >> contentlen >> content;
|
|
|
|
recvData >> typelen >> type;
|
|
|
|
if (suggestion == 0)
|
|
TC_LOG_DEBUG("network", "WORLD: Received CMSG_BUG [Bug Report]");
|
|
else
|
|
TC_LOG_DEBUG("network", "WORLD: Received CMSG_BUG [Suggestion]");
|
|
|
|
TC_LOG_DEBUG("network", "%s", type.c_str());
|
|
TC_LOG_DEBUG("network", "%s", content.c_str());
|
|
|
|
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_BUG_REPORT);
|
|
|
|
stmt->setString(0, type);
|
|
stmt->setString(1, content);
|
|
|
|
CharacterDatabase.Execute(stmt);
|
|
}
|
|
|
|
void WorldSession::HandleReclaimCorpseOpcode(WorldPacket& recvData)
|
|
{
|
|
TC_LOG_DEBUG("network", "WORLD: Received CMSG_RECLAIM_CORPSE");
|
|
|
|
ObjectGuid guid;
|
|
recvData >> guid;
|
|
|
|
if (_player->IsAlive())
|
|
return;
|
|
|
|
// do not allow corpse reclaim in arena
|
|
if (_player->InArena())
|
|
return;
|
|
|
|
// body not released yet
|
|
if (!_player->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
|
|
return;
|
|
|
|
Corpse* corpse = _player->GetCorpse();
|
|
if (!corpse)
|
|
return;
|
|
|
|
// prevent resurrect before 30-sec delay after body release not finished
|
|
if (time_t(corpse->GetGhostTime() + _player->GetCorpseReclaimDelay(corpse->GetType() == CORPSE_RESURRECTABLE_PVP)) > time_t(time(NULL)))
|
|
return;
|
|
|
|
if (!corpse->IsWithinDistInMap(_player, CORPSE_RECLAIM_RADIUS, true))
|
|
return;
|
|
|
|
// resurrect
|
|
_player->ResurrectPlayer(_player->InBattleground() ? 1.0f : 0.5f);
|
|
|
|
// spawn bones
|
|
_player->SpawnCorpseBones();
|
|
}
|
|
|
|
void WorldSession::HandleResurrectResponseOpcode(WorldPacket& recvData)
|
|
{
|
|
TC_LOG_DEBUG("network", "WORLD: Received CMSG_RESURRECT_RESPONSE");
|
|
|
|
ObjectGuid guid;
|
|
uint8 status;
|
|
recvData >> guid;
|
|
recvData >> status;
|
|
|
|
if (GetPlayer()->IsAlive())
|
|
return;
|
|
|
|
if (status == 0)
|
|
{
|
|
GetPlayer()->clearResurrectRequestData(); // reject
|
|
return;
|
|
}
|
|
|
|
if (GetPlayer()->IsValidGhoulResurrectRequest(guid))
|
|
{
|
|
GetPlayer()->GhoulResurrect();
|
|
return;
|
|
}
|
|
|
|
if (!GetPlayer()->isResurrectRequestedBy(guid))
|
|
return;
|
|
|
|
GetPlayer()->ResurrectUsingRequestData();
|
|
}
|
|
|
|
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::HandleAreaTriggerOpcode(WorldPacket& recvData)
|
|
{
|
|
uint32 triggerId;
|
|
recvData >> triggerId;
|
|
|
|
TC_LOG_DEBUG("network", "CMSG_AREATRIGGER. Trigger ID: %u", triggerId);
|
|
|
|
Player* player = GetPlayer();
|
|
if (player->IsInFlight())
|
|
{
|
|
TC_LOG_DEBUG("network", "HandleAreaTriggerOpcode: Player '%s' (GUID: %u) in flight, ignore Area Trigger ID:%u",
|
|
player->GetName().c_str(), player->GetGUID().GetCounter(), triggerId);
|
|
return;
|
|
}
|
|
|
|
AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(triggerId);
|
|
if (!atEntry)
|
|
{
|
|
TC_LOG_DEBUG("network", "HandleAreaTriggerOpcode: Player '%s' (GUID: %u) send unknown (by DBC) Area Trigger ID:%u",
|
|
player->GetName().c_str(), player->GetGUID().GetCounter(), triggerId);
|
|
return;
|
|
}
|
|
|
|
if (!player->IsInAreaTriggerRadius(atEntry))
|
|
{
|
|
TC_LOG_DEBUG("network", "HandleAreaTriggerOpcode: Player '%s' (GUID: %u) too far, ignore Area Trigger ID: %u",
|
|
player->GetName().c_str(), player->GetGUID().GetCounter(), triggerId);
|
|
return;
|
|
}
|
|
|
|
if (player->isDebugAreaTriggers)
|
|
ChatHandler(player->GetSession()).PSendSysMessage(LANG_DEBUG_AREATRIGGER_REACHED, triggerId);
|
|
|
|
if (sScriptMgr->OnAreaTrigger(player, atEntry))
|
|
return;
|
|
|
|
if (player->IsAlive())
|
|
if (uint32 questId = sObjectMgr->GetQuestForAreaTrigger(triggerId))
|
|
if (player->GetQuestStatus(questId) == QUEST_STATUS_INCOMPLETE)
|
|
player->AreaExploredOrEventHappens(questId);
|
|
|
|
if (sObjectMgr->IsTavernAreaTrigger(triggerId))
|
|
{
|
|
// set resting flag we are in the inn
|
|
player->SetRestFlag(REST_FLAG_IN_TAVERN, atEntry->id);
|
|
|
|
if (sWorld->IsFFAPvPRealm())
|
|
player->RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP);
|
|
|
|
return;
|
|
}
|
|
|
|
if (Battleground* bg = player->GetBattleground())
|
|
if (bg->GetStatus() == STATUS_IN_PROGRESS)
|
|
bg->HandleAreaTrigger(player, triggerId);
|
|
|
|
if (OutdoorPvP* pvp = player->GetOutdoorPvP())
|
|
if (pvp->HandleAreaTrigger(_player, triggerId))
|
|
return;
|
|
|
|
AreaTrigger const* at = sObjectMgr->GetAreaTrigger(triggerId);
|
|
if (!at)
|
|
return;
|
|
|
|
bool teleported = false;
|
|
if (player->GetMapId() != at->target_mapId)
|
|
{
|
|
if (Map::EnterState denyReason = sMapMgr->PlayerCannotEnter(at->target_mapId, player, false))
|
|
{
|
|
bool reviveAtTrigger = false; // should we revive the player if he is trying to enter the correct instance?
|
|
switch (denyReason)
|
|
{
|
|
case Map::CANNOT_ENTER_NO_ENTRY:
|
|
TC_LOG_DEBUG("maps", "MAP: Player '%s' attempted to enter map with id %d which has no entry", player->GetName().c_str(), at->target_mapId);
|
|
break;
|
|
case Map::CANNOT_ENTER_UNINSTANCED_DUNGEON:
|
|
TC_LOG_DEBUG("maps", "MAP: Player '%s' attempted to enter dungeon map %d but no instance template was found", player->GetName().c_str(), at->target_mapId);
|
|
break;
|
|
case Map::CANNOT_ENTER_DIFFICULTY_UNAVAILABLE:
|
|
TC_LOG_DEBUG("maps", "MAP: Player '%s' attempted to enter instance map %d but the requested difficulty was not found", player->GetName().c_str(), at->target_mapId);
|
|
if (MapEntry const* entry = sMapStore.LookupEntry(at->target_mapId))
|
|
player->SendTransferAborted(entry->MapID, TRANSFER_ABORT_DIFFICULTY, player->GetDifficulty(entry->IsRaid()));
|
|
break;
|
|
case Map::CANNOT_ENTER_NOT_IN_RAID:
|
|
if (MapEntry const* entry = sMapStore.LookupEntry(at->target_mapId))
|
|
{
|
|
char const* mapName = entry->name[player->GetSession()->GetSessionDbcLocale()];
|
|
TC_LOG_DEBUG("maps", "MAP: Player '%s' must be in a raid group to enter instance '%s'", player->GetName().c_str(), mapName);
|
|
// probably there must be special opcode, because client has this string constant in GlobalStrings.lua
|
|
player->GetSession()->SendAreaTriggerMessage(player->GetSession()->GetTrinityString(LANG_INSTANCE_RAID_GROUP_ONLY), mapName);
|
|
}
|
|
reviveAtTrigger = true;
|
|
break;
|
|
case Map::CANNOT_ENTER_CORPSE_IN_DIFFERENT_INSTANCE:
|
|
{
|
|
WorldPacket data(SMSG_CORPSE_NOT_IN_INSTANCE);
|
|
player->GetSession()->SendPacket(&data);
|
|
TC_LOG_DEBUG("maps", "MAP: Player '%s' does not have a corpse in instance map %d and cannot enter", player->GetName().c_str(), at->target_mapId);
|
|
break;
|
|
}
|
|
case Map::CANNOT_ENTER_INSTANCE_BIND_MISMATCH:
|
|
if (MapEntry const* entry = sMapStore.LookupEntry(at->target_mapId))
|
|
{
|
|
char const* mapName = entry->name[player->GetSession()->GetSessionDbcLocale()];
|
|
TC_LOG_DEBUG("maps", "MAP: Player '%s' cannot enter instance map '%s' because their permanent bind is incompatible with their group's", player->GetName().c_str(), mapName);
|
|
// is there a special opcode for this?
|
|
// @todo figure out how to get player localized difficulty string (e.g. "10 player", "Heroic" etc)
|
|
ChatHandler(player->GetSession()).PSendSysMessage(player->GetSession()->GetTrinityString(LANG_INSTANCE_BIND_MISMATCH), mapName);
|
|
}
|
|
reviveAtTrigger = true;
|
|
break;
|
|
case Map::CANNOT_ENTER_TOO_MANY_INSTANCES:
|
|
player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_TOO_MANY_INSTANCES);
|
|
TC_LOG_DEBUG("maps", "MAP: Player '%s' cannot enter instance map %d because he has exceeded the maximum number of instances per hour.", player->GetName().c_str(), at->target_mapId);
|
|
reviveAtTrigger = true;
|
|
break;
|
|
case Map::CANNOT_ENTER_MAX_PLAYERS:
|
|
player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_MAX_PLAYERS);
|
|
reviveAtTrigger = true;
|
|
break;
|
|
case Map::CANNOT_ENTER_ZONE_IN_COMBAT:
|
|
player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_ZONE_IN_COMBAT);
|
|
reviveAtTrigger = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (reviveAtTrigger) // check if the player is touching the areatrigger leading to the map his corpse is on
|
|
if (!player->IsAlive() && player->HasCorpse())
|
|
if (player->GetCorpseLocation().GetMapId() == at->target_mapId)
|
|
{
|
|
player->ResurrectPlayer(0.5f);
|
|
player->SpawnCorpseBones();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (Group* group = player->GetGroup())
|
|
if (group->isLFGGroup() && player->GetMap()->IsDungeon())
|
|
teleported = player->TeleportToBGEntryPoint();
|
|
}
|
|
|
|
if (!teleported)
|
|
player->TeleportTo(at->target_mapId, at->target_X, at->target_Y, at->target_Z, at->target_Orientation, TELE_TO_NOT_LEAVE_TRANSPORT);
|
|
}
|
|
|
|
void WorldSession::HandleUpdateAccountData(WorldPacket& recvData)
|
|
{
|
|
TC_LOG_DEBUG("network", "WORLD: Received CMSG_UPDATE_ACCOUNT_DATA");
|
|
|
|
uint32 type, timestamp, decompressedSize;
|
|
recvData >> type >> timestamp >> decompressedSize;
|
|
|
|
TC_LOG_DEBUG("network", "UAD: type %u, time %u, decompressedSize %u", type, timestamp, decompressedSize);
|
|
|
|
if (type > NUM_ACCOUNT_DATA_TYPES)
|
|
return;
|
|
|
|
if (decompressedSize == 0) // erase
|
|
{
|
|
SetAccountData(AccountDataType(type), 0, "");
|
|
|
|
WorldPacket data(SMSG_UPDATE_ACCOUNT_DATA_COMPLETE, 4+4);
|
|
data << uint32(type);
|
|
data << uint32(0);
|
|
SendPacket(&data);
|
|
|
|
return;
|
|
}
|
|
|
|
if (decompressedSize > 0xFFFF)
|
|
{
|
|
recvData.rfinish(); // unnneded warning spam in this case
|
|
TC_LOG_ERROR("network", "UAD: Account data packet too big, size %u", decompressedSize);
|
|
return;
|
|
}
|
|
|
|
ByteBuffer dest;
|
|
dest.resize(decompressedSize);
|
|
|
|
uLongf realSize = decompressedSize;
|
|
if (uncompress(dest.contents(), &realSize, recvData.contents() + recvData.rpos(), recvData.size() - recvData.rpos()) != Z_OK)
|
|
{
|
|
recvData.rfinish(); // unnneded warning spam in this case
|
|
TC_LOG_ERROR("network", "UAD: Failed to decompress account data");
|
|
return;
|
|
}
|
|
|
|
recvData.rfinish(); // uncompress read (recvData.size() - recvData.rpos())
|
|
|
|
std::string adata;
|
|
dest >> adata;
|
|
|
|
SetAccountData(AccountDataType(type), timestamp, adata);
|
|
|
|
WorldPacket data(SMSG_UPDATE_ACCOUNT_DATA_COMPLETE, 4+4);
|
|
data << uint32(type);
|
|
data << uint32(0);
|
|
SendPacket(&data);
|
|
}
|
|
|
|
void WorldSession::HandleRequestAccountData(WorldPacket& recvData)
|
|
{
|
|
TC_LOG_DEBUG("network", "WORLD: Received CMSG_REQUEST_ACCOUNT_DATA");
|
|
|
|
uint32 type;
|
|
recvData >> type;
|
|
|
|
TC_LOG_DEBUG("network", "RAD: type %u", type);
|
|
|
|
if (type >= NUM_ACCOUNT_DATA_TYPES)
|
|
return;
|
|
|
|
AccountData* adata = GetAccountData(AccountDataType(type));
|
|
|
|
uint32 size = adata->Data.size();
|
|
|
|
uLongf destSize = compressBound(size);
|
|
|
|
ByteBuffer dest;
|
|
dest.resize(destSize);
|
|
|
|
if (size && compress(dest.contents(), &destSize, (uint8 const*)adata->Data.c_str(), size) != Z_OK)
|
|
{
|
|
TC_LOG_DEBUG("network", "RAD: Failed to compress account data");
|
|
return;
|
|
}
|
|
|
|
dest.resize(destSize);
|
|
|
|
WorldPacket data(SMSG_UPDATE_ACCOUNT_DATA, 8+4+4+4+destSize);
|
|
data << uint64(_player ? _player->GetGUID() : ObjectGuid::Empty);
|
|
data << uint32(type); // type (0-7)
|
|
data << uint32(adata->Time); // unix time
|
|
data << uint32(size); // decompressed length
|
|
data.append(dest); // compressed data
|
|
SendPacket(&data);
|
|
}
|
|
|
|
void WorldSession::HandleSetActionButtonOpcode(WorldPacket& recvData)
|
|
{
|
|
uint8 button;
|
|
uint32 packetData;
|
|
recvData >> button >> packetData;
|
|
TC_LOG_DEBUG("network", "CMSG_SET_ACTION_BUTTON Button: %u Data: %u", button, packetData);
|
|
|
|
if (!packetData)
|
|
GetPlayer()->removeActionButton(button);
|
|
else
|
|
GetPlayer()->addActionButton(button, ACTION_BUTTON_ACTION(packetData), ACTION_BUTTON_TYPE(packetData));
|
|
}
|
|
|
|
void WorldSession::HandleCompleteCinematic(WorldPacket& /*recvData*/)
|
|
{
|
|
TC_LOG_DEBUG("network", "WORLD: Received CMSG_COMPLETE_CINEMATIC");
|
|
}
|
|
|
|
void WorldSession::HandleNextCinematicCamera(WorldPacket& /*recvData*/)
|
|
{
|
|
TC_LOG_DEBUG("network", "WORLD: Received CMSG_NEXT_CINEMATIC_CAMERA");
|
|
}
|
|
|
|
void WorldSession::HandleMoveTimeSkippedOpcode(WorldPacket& recvData)
|
|
{
|
|
/* WorldSession::Update(getMSTime());*/
|
|
TC_LOG_DEBUG("network", "WORLD: Received CMSG_MOVE_TIME_SKIPPED");
|
|
|
|
ObjectGuid guid;
|
|
recvData >> guid.ReadAsPacked();
|
|
recvData.read_skip<uint32>();
|
|
/*
|
|
uint64 guid;
|
|
uint32 time_skipped;
|
|
recvData >> guid;
|
|
recvData >> time_skipped;
|
|
TC_LOG_DEBUG("network", "WORLD: CMSG_MOVE_TIME_SKIPPED");
|
|
|
|
//// @todo
|
|
must be need use in Trinity
|
|
We substract server Lags to move time (AntiLags)
|
|
for exmaple
|
|
GetPlayer()->ModifyLastMoveTime(-int32(time_skipped));
|
|
*/
|
|
}
|
|
|
|
void WorldSession::HandleFeatherFallAck(WorldPacket& recvData)
|
|
{
|
|
TC_LOG_DEBUG("network", "WORLD: CMSG_MOVE_FEATHER_FALL_ACK");
|
|
|
|
// no used
|
|
recvData.rfinish(); // prevent warnings spam
|
|
}
|
|
|
|
void WorldSession::HandleMoveUnRootAck(WorldPacket& recvData)
|
|
{
|
|
// no used
|
|
recvData.rfinish(); // prevent warnings spam
|
|
/*
|
|
uint64 guid;
|
|
recvData >> guid;
|
|
|
|
// now can skip not our packet
|
|
if (_player->GetGUID() != guid)
|
|
{
|
|
recvData.rfinish(); // prevent warnings spam
|
|
return;
|
|
}
|
|
|
|
TC_LOG_DEBUG("network", "WORLD: CMSG_FORCE_MOVE_UNROOT_ACK");
|
|
|
|
recvData.read_skip<uint32>(); // unk
|
|
|
|
MovementInfo movementInfo;
|
|
movementInfo.guid = guid;
|
|
ReadMovementInfo(recvData, &movementInfo);
|
|
recvData.read_skip<float>(); // unk2
|
|
*/
|
|
}
|
|
|
|
void WorldSession::HandleMoveRootAck(WorldPacket& recvData)
|
|
{
|
|
// no used
|
|
recvData.rfinish(); // prevent warnings spam
|
|
/*
|
|
uint64 guid;
|
|
recvData >> guid;
|
|
|
|
// now can skip not our packet
|
|
if (_player->GetGUID() != guid)
|
|
{
|
|
recvData.rfinish(); // prevent warnings spam
|
|
return;
|
|
}
|
|
|
|
TC_LOG_DEBUG("network", "WORLD: CMSG_FORCE_MOVE_ROOT_ACK");
|
|
|
|
recvData.read_skip<uint32>(); // unk
|
|
|
|
MovementInfo movementInfo;
|
|
ReadMovementInfo(recvData, &movementInfo);
|
|
*/
|
|
}
|
|
|
|
void WorldSession::HandleSetActionBarToggles(WorldPacket& recvData)
|
|
{
|
|
uint8 actionBar;
|
|
recvData >> actionBar;
|
|
|
|
if (!GetPlayer()) // ignore until not logged (check needed because STATUS_AUTHED)
|
|
{
|
|
if (actionBar != 0)
|
|
TC_LOG_ERROR("network", "WorldSession::HandleSetActionBarToggles in not logged state with value: %u, ignored", uint32(actionBar));
|
|
return;
|
|
}
|
|
|
|
GetPlayer()->SetByteValue(PLAYER_FIELD_BYTES, 2, actionBar);
|
|
}
|
|
|
|
void WorldSession::HandlePlayedTime(WorldPacket& recvData)
|
|
{
|
|
uint8 unk1;
|
|
recvData >> unk1; // 0 or 1 expected
|
|
|
|
WorldPacket data(SMSG_PLAYED_TIME, 4 + 4 + 1);
|
|
data << uint32(_player->GetTotalPlayedTime());
|
|
data << uint32(_player->GetLevelPlayedTime());
|
|
data << uint8(unk1); // 0 - will not show in chat frame
|
|
SendPacket(&data);
|
|
}
|
|
|
|
void WorldSession::HandleInspectOpcode(WorldPacket& recvData)
|
|
{
|
|
ObjectGuid guid;
|
|
recvData >> guid;
|
|
|
|
TC_LOG_DEBUG("network", "WORLD: Received CMSG_INSPECT");
|
|
|
|
Player* player = ObjectAccessor::FindPlayer(guid);
|
|
if (!player)
|
|
{
|
|
TC_LOG_DEBUG("network", "CMSG_INSPECT: No player found from %s", guid.ToString().c_str());
|
|
return;
|
|
}
|
|
|
|
if (!GetPlayer()->IsWithinDistInMap(player, INSPECT_DISTANCE, false))
|
|
return;
|
|
|
|
if (GetPlayer()->IsValidAttackTarget(player))
|
|
return;
|
|
|
|
uint32 talent_points = 0x47;
|
|
uint32 guid_size = player->GetPackGUID().size();
|
|
WorldPacket data(SMSG_INSPECT_TALENT, guid_size+4+talent_points);
|
|
data << player->GetPackGUID();
|
|
|
|
if (GetPlayer()->CanBeGameMaster() || sWorld->getIntConfig(CONFIG_TALENTS_INSPECTING) + (GetPlayer()->GetTeamId() == player->GetTeamId()) > 1)
|
|
player->BuildPlayerTalentsInfoData(&data);
|
|
else
|
|
{
|
|
data << uint32(0); // unspentTalentPoints
|
|
data << uint8(0); // talentGroupCount
|
|
data << uint8(0); // talentGroupIndex
|
|
}
|
|
|
|
player->BuildEnchantmentsInfoData(&data);
|
|
SendPacket(&data);
|
|
}
|
|
|
|
void WorldSession::HandleInspectHonorStatsOpcode(WorldPacket& recvData)
|
|
{
|
|
ObjectGuid guid;
|
|
recvData >> guid;
|
|
|
|
Player* player = ObjectAccessor::FindPlayer(guid);
|
|
|
|
if (!player)
|
|
{
|
|
TC_LOG_DEBUG("network", "MSG_INSPECT_HONOR_STATS: No player found from %s", guid.ToString().c_str());
|
|
return;
|
|
}
|
|
|
|
if (!GetPlayer()->IsWithinDistInMap(player, INSPECT_DISTANCE, false))
|
|
return;
|
|
|
|
if (GetPlayer()->IsValidAttackTarget(player))
|
|
return;
|
|
|
|
WorldPacket data(MSG_INSPECT_HONOR_STATS, 8+1+4*4);
|
|
data << uint64(player->GetGUID());
|
|
data << uint8(player->GetHonorPoints());
|
|
data << uint32(player->GetUInt32Value(PLAYER_FIELD_KILLS));
|
|
data << uint32(player->GetUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION));
|
|
data << uint32(player->GetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION));
|
|
data << uint32(player->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS));
|
|
SendPacket(&data);
|
|
}
|
|
|
|
void WorldSession::HandleWorldTeleportOpcode(WorldPacket& recvData)
|
|
{
|
|
uint32 time;
|
|
uint32 mapid;
|
|
float PositionX;
|
|
float PositionY;
|
|
float PositionZ;
|
|
float Orientation;
|
|
|
|
recvData >> time; // time in m.sec.
|
|
recvData >> mapid;
|
|
recvData >> PositionX;
|
|
recvData >> PositionY;
|
|
recvData >> PositionZ;
|
|
recvData >> Orientation; // o (3.141593 = 180 degrees)
|
|
|
|
TC_LOG_DEBUG("network", "WORLD: Received CMSG_WORLD_TELEPORT");
|
|
|
|
if (GetPlayer()->IsInFlight())
|
|
{
|
|
TC_LOG_DEBUG("network", "Player '%s' (GUID: %u) in flight, ignore worldport command.",
|
|
GetPlayer()->GetName().c_str(), GetPlayer()->GetGUID().GetCounter());
|
|
return;
|
|
}
|
|
|
|
TC_LOG_DEBUG("network", "CMSG_WORLD_TELEPORT: Player = %s, Time = %u, map = %u, x = %f, y = %f, z = %f, o = %f",
|
|
GetPlayer()->GetName().c_str(), time, mapid, PositionX, PositionY, PositionZ, Orientation);
|
|
|
|
if (HasPermission(rbac::RBAC_PERM_OPCODE_WORLD_TELEPORT))
|
|
GetPlayer()->TeleportTo(mapid, PositionX, PositionY, PositionZ, Orientation);
|
|
else
|
|
SendNotification(LANG_YOU_NOT_HAVE_PERMISSION);
|
|
}
|
|
|
|
void WorldSession::HandleWhoisOpcode(WorldPacket& recvData)
|
|
{
|
|
TC_LOG_DEBUG("network", "Received opcode CMSG_WHOIS");
|
|
std::string charname;
|
|
recvData >> charname;
|
|
|
|
if (!HasPermission(rbac::RBAC_PERM_OPCODE_WHOIS))
|
|
{
|
|
SendNotification(LANG_YOU_NOT_HAVE_PERMISSION);
|
|
return;
|
|
}
|
|
|
|
if (charname.empty() || !normalizePlayerName (charname))
|
|
{
|
|
SendNotification(LANG_NEED_CHARACTER_NAME);
|
|
return;
|
|
}
|
|
|
|
Player* player = ObjectAccessor::FindConnectedPlayerByName(charname);
|
|
|
|
if (!player)
|
|
{
|
|
SendNotification(LANG_PLAYER_NOT_EXIST_OR_OFFLINE, charname.c_str());
|
|
return;
|
|
}
|
|
|
|
uint32 accid = player->GetSession()->GetAccountId();
|
|
|
|
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_WHOIS);
|
|
|
|
stmt->setUInt32(0, accid);
|
|
|
|
PreparedQueryResult result = LoginDatabase.Query(stmt);
|
|
|
|
if (!result)
|
|
{
|
|
SendNotification(LANG_ACCOUNT_FOR_PLAYER_NOT_FOUND, charname.c_str());
|
|
return;
|
|
}
|
|
|
|
Field* fields = result->Fetch();
|
|
std::string acc = fields[0].GetString();
|
|
if (acc.empty())
|
|
acc = "Unknown";
|
|
std::string email = fields[1].GetString();
|
|
if (email.empty())
|
|
email = "Unknown";
|
|
std::string lastip = fields[2].GetString();
|
|
if (lastip.empty())
|
|
lastip = "Unknown";
|
|
|
|
std::string msg = charname + "'s " + "account is " + acc + ", e-mail: " + email + ", last ip: " + lastip;
|
|
|
|
WorldPacket data(SMSG_WHOIS, msg.size()+1);
|
|
data << msg;
|
|
SendPacket(&data);
|
|
|
|
TC_LOG_DEBUG("network", "Received whois command from player %s for character %s",
|
|
GetPlayer()->GetName().c_str(), charname.c_str());
|
|
}
|
|
|
|
void WorldSession::HandleComplainOpcode(WorldPacket& recvData)
|
|
{
|
|
TC_LOG_DEBUG("network", "WORLD: CMSG_COMPLAIN");
|
|
|
|
uint8 spam_type; // 0 - mail, 1 - chat
|
|
ObjectGuid spammer_guid;
|
|
uint32 unk1 = 0;
|
|
uint32 unk2 = 0;
|
|
uint32 unk3 = 0;
|
|
uint32 unk4 = 0;
|
|
std::string description = "";
|
|
recvData >> spam_type; // unk 0x01 const, may be spam type (mail/chat)
|
|
recvData >> spammer_guid; // player guid
|
|
switch (spam_type)
|
|
{
|
|
case 0:
|
|
recvData >> unk1; // const 0
|
|
recvData >> unk2; // probably mail id
|
|
recvData >> unk3; // const 0
|
|
break;
|
|
case 1:
|
|
recvData >> unk1; // probably language
|
|
recvData >> unk2; // message type?
|
|
recvData >> unk3; // probably channel id
|
|
recvData >> unk4; // time
|
|
recvData >> description; // spam description string (messagetype, channel name, player name, message)
|
|
break;
|
|
}
|
|
|
|
// NOTE: all chat messages from this spammer automatically ignored by spam reporter until logout in case chat spam.
|
|
// if it's mail spam - ALL mails from this spammer automatically removed by client
|
|
|
|
// Complaint Received message
|
|
WorldPacket data(SMSG_COMPLAIN_RESULT, 1);
|
|
data << uint8(0);
|
|
SendPacket(&data);
|
|
|
|
TC_LOG_DEBUG("network", "REPORT SPAM: type %u, %s, unk1 %u, unk2 %u, unk3 %u, unk4 %u, message %s",
|
|
spam_type, spammer_guid.ToString().c_str(), unk1, unk2, unk3, unk4, description.c_str());
|
|
}
|
|
|
|
void WorldSession::HandleRealmSplitOpcode(WorldPacket& recvData)
|
|
{
|
|
TC_LOG_DEBUG("network", "CMSG_REALM_SPLIT");
|
|
|
|
uint32 unk;
|
|
std::string split_date = "01/01/01";
|
|
recvData >> unk;
|
|
|
|
WorldPacket data(SMSG_REALM_SPLIT, 4+4+split_date.size()+1);
|
|
data << unk;
|
|
data << uint32(0x00000000); // realm split state
|
|
// split states:
|
|
// 0x0 realm normal
|
|
// 0x1 realm split
|
|
// 0x2 realm split pending
|
|
data << split_date;
|
|
SendPacket(&data);
|
|
//TC_LOG_DEBUG("response sent %u", unk);
|
|
}
|
|
|
|
void WorldSession::HandleFarSightOpcode(WorldPacket& recvData)
|
|
{
|
|
TC_LOG_DEBUG("network", "WORLD: CMSG_FAR_SIGHT");
|
|
|
|
bool apply;
|
|
recvData >> apply;
|
|
|
|
if (apply)
|
|
{
|
|
TC_LOG_DEBUG("network", "Added FarSight %s to player %u", _player->GetGuidValue(PLAYER_FARSIGHT).ToString().c_str(), _player->GetGUID().GetCounter());
|
|
if (WorldObject* target = _player->GetViewpoint())
|
|
_player->SetSeer(target);
|
|
else
|
|
TC_LOG_ERROR("network", "Player %s (%s) requests non-existing seer %s", _player->GetName().c_str(), _player->GetGUID().ToString().c_str(), _player->GetGuidValue(PLAYER_FARSIGHT).ToString().c_str());
|
|
}
|
|
else
|
|
{
|
|
TC_LOG_DEBUG("network", "Player %u set vision to self", _player->GetGUID().GetCounter());
|
|
_player->SetSeer(_player);
|
|
}
|
|
|
|
GetPlayer()->UpdateVisibilityForPlayer();
|
|
}
|
|
|
|
void WorldSession::HandleSetTitleOpcode(WorldPacket& recvData)
|
|
{
|
|
TC_LOG_DEBUG("network", "CMSG_SET_TITLE");
|
|
|
|
int32 title;
|
|
recvData >> title;
|
|
|
|
// -1 at none
|
|
if (title > 0 && title < MAX_TITLE_INDEX)
|
|
{
|
|
if (!GetPlayer()->HasTitle(title))
|
|
return;
|
|
}
|
|
else
|
|
title = 0;
|
|
|
|
GetPlayer()->SetUInt32Value(PLAYER_CHOSEN_TITLE, title);
|
|
}
|
|
|
|
void WorldSession::HandleTimeSyncResp(WorldPacket& recvData)
|
|
{
|
|
TC_LOG_DEBUG("network", "CMSG_TIME_SYNC_RESP");
|
|
|
|
uint32 counter, clientTicks;
|
|
recvData >> counter >> clientTicks;
|
|
|
|
if (counter != _player->m_timeSyncCounter - 1)
|
|
TC_LOG_DEBUG("network", "Wrong time sync counter from player %s (cheater?)", _player->GetName().c_str());
|
|
|
|
TC_LOG_DEBUG("network", "Time sync received: counter %u, client ticks %u, time since last sync %u", counter, clientTicks, clientTicks - _player->m_timeSyncClient);
|
|
|
|
uint32 ourTicks = clientTicks + (getMSTime() - _player->m_timeSyncServer);
|
|
|
|
// diff should be small
|
|
TC_LOG_DEBUG("network", "Our ticks: %u, diff %u, latency %u", ourTicks, ourTicks - clientTicks, GetLatency());
|
|
|
|
_player->m_timeSyncClient = clientTicks;
|
|
}
|
|
|
|
void WorldSession::HandleResetInstancesOpcode(WorldPacket& /*recvData*/)
|
|
{
|
|
TC_LOG_DEBUG("network", "WORLD: CMSG_RESET_INSTANCES");
|
|
|
|
if (Group* group = _player->GetGroup())
|
|
{
|
|
if (group->IsLeader(_player->GetGUID()))
|
|
group->ResetInstances(INSTANCE_RESET_ALL, false, _player);
|
|
}
|
|
else
|
|
_player->ResetInstances(INSTANCE_RESET_ALL, false);
|
|
}
|
|
|
|
void WorldSession::HandleSetDungeonDifficultyOpcode(WorldPacket& recvData)
|
|
{
|
|
TC_LOG_DEBUG("network", "MSG_SET_DUNGEON_DIFFICULTY");
|
|
|
|
uint32 mode;
|
|
recvData >> mode;
|
|
|
|
if (mode >= MAX_DUNGEON_DIFFICULTY)
|
|
{
|
|
TC_LOG_DEBUG("network", "WorldSession::HandleSetDungeonDifficultyOpcode: player %d sent an invalid instance mode %d!", _player->GetGUID().GetCounter(), mode);
|
|
return;
|
|
}
|
|
|
|
if (Difficulty(mode) == _player->GetDungeonDifficulty())
|
|
return;
|
|
|
|
// cannot reset while in an instance
|
|
Map* map = _player->FindMap();
|
|
if (map && map->IsDungeon())
|
|
{
|
|
TC_LOG_DEBUG("network", "WorldSession::HandleSetDungeonDifficultyOpcode: player (Name: %s, GUID: %u) tried to reset the instance while player is inside!",
|
|
_player->GetName().c_str(), _player->GetGUID().GetCounter());
|
|
return;
|
|
}
|
|
|
|
Group* group = _player->GetGroup();
|
|
if (group)
|
|
{
|
|
if (group->IsLeader(_player->GetGUID()))
|
|
{
|
|
for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
|
|
{
|
|
Player* groupGuy = itr->GetSource();
|
|
if (!groupGuy)
|
|
continue;
|
|
|
|
if (!groupGuy->IsInMap(groupGuy))
|
|
return;
|
|
|
|
if (groupGuy->GetMap()->IsNonRaidDungeon())
|
|
{
|
|
TC_LOG_DEBUG("network", "WorldSession::HandleSetDungeonDifficultyOpcode: player %d tried to reset the instance while group member (Name: %s, GUID: %u) is inside!",
|
|
_player->GetGUID().GetCounter(), groupGuy->GetName().c_str(), groupGuy->GetGUID().GetCounter());
|
|
return;
|
|
}
|
|
}
|
|
// the difficulty is set even if the instances can't be reset
|
|
//_player->SendDungeonDifficulty(true);
|
|
group->ResetInstances(INSTANCE_RESET_CHANGE_DIFFICULTY, false, _player);
|
|
group->SetDungeonDifficulty(Difficulty(mode));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_player->ResetInstances(INSTANCE_RESET_CHANGE_DIFFICULTY, false);
|
|
_player->SetDungeonDifficulty(Difficulty(mode));
|
|
}
|
|
}
|
|
|
|
void WorldSession::HandleSetRaidDifficultyOpcode(WorldPacket& recvData)
|
|
{
|
|
TC_LOG_DEBUG("network", "MSG_SET_RAID_DIFFICULTY");
|
|
|
|
uint32 mode;
|
|
recvData >> mode;
|
|
|
|
if (mode >= MAX_RAID_DIFFICULTY)
|
|
{
|
|
TC_LOG_ERROR("network", "WorldSession::HandleSetRaidDifficultyOpcode: player %d sent an invalid instance mode %d!", _player->GetGUID().GetCounter(), mode);
|
|
return;
|
|
}
|
|
|
|
// cannot reset while in an instance
|
|
Map* map = _player->FindMap();
|
|
if (map && map->IsDungeon())
|
|
{
|
|
TC_LOG_DEBUG("network", "WorldSession::HandleSetRaidDifficultyOpcode: player %d tried to reset the instance while inside!", _player->GetGUID().GetCounter());
|
|
return;
|
|
}
|
|
|
|
if (Difficulty(mode) == _player->GetRaidDifficulty())
|
|
return;
|
|
|
|
Group* group = _player->GetGroup();
|
|
if (group)
|
|
{
|
|
if (group->IsLeader(_player->GetGUID()))
|
|
{
|
|
for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
|
|
{
|
|
Player* groupGuy = itr->GetSource();
|
|
if (!groupGuy)
|
|
continue;
|
|
|
|
if (!groupGuy->IsInMap(groupGuy))
|
|
return;
|
|
|
|
if (groupGuy->GetMap()->IsRaid())
|
|
{
|
|
TC_LOG_DEBUG("network", "WorldSession::HandleSetRaidDifficultyOpcode: player %d tried to reset the instance while inside!", _player->GetGUID().GetCounter());
|
|
return;
|
|
}
|
|
}
|
|
// the difficulty is set even if the instances can't be reset
|
|
//_player->SendDungeonDifficulty(true);
|
|
group->ResetInstances(INSTANCE_RESET_CHANGE_DIFFICULTY, true, _player);
|
|
group->SetRaidDifficulty(Difficulty(mode));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_player->ResetInstances(INSTANCE_RESET_CHANGE_DIFFICULTY, true);
|
|
_player->SetRaidDifficulty(Difficulty(mode));
|
|
}
|
|
}
|
|
|
|
void WorldSession::HandleCancelMountAuraOpcode(WorldPacket& /*recvData*/)
|
|
{
|
|
TC_LOG_DEBUG("network", "WORLD: CMSG_CANCEL_MOUNT_AURA");
|
|
|
|
//If player is not mounted, so go out :)
|
|
if (!_player->IsMounted()) // not blizz like; no any messages on blizz
|
|
{
|
|
ChatHandler(this).SendSysMessage(LANG_CHAR_NON_MOUNTED);
|
|
return;
|
|
}
|
|
|
|
if (_player->IsInFlight()) // not blizz like; no any messages on blizz
|
|
{
|
|
ChatHandler(this).SendSysMessage(LANG_YOU_IN_FLIGHT);
|
|
return;
|
|
}
|
|
|
|
_player->RemoveAurasByType(SPELL_AURA_MOUNTED); // Calls Dismount()
|
|
}
|
|
|
|
void WorldSession::HandleMoveSetCanFlyAckOpcode(WorldPacket& recvData)
|
|
{
|
|
// fly mode on/off
|
|
TC_LOG_DEBUG("network", "WORLD: CMSG_MOVE_SET_CAN_FLY_ACK");
|
|
|
|
ObjectGuid guid; // guid - unused
|
|
recvData >> guid.ReadAsPacked();
|
|
|
|
recvData.read_skip<uint32>(); // unk
|
|
|
|
MovementInfo movementInfo;
|
|
movementInfo.guid = guid;
|
|
ReadMovementInfo(recvData, &movementInfo);
|
|
|
|
recvData.read_skip<float>(); // unk2
|
|
|
|
_player->m_mover->m_movementInfo.flags = movementInfo.GetMovementFlags();
|
|
}
|
|
|
|
void WorldSession::HandleRequestPetInfoOpcode(WorldPacket& /*recvData */)
|
|
{
|
|
/*
|
|
TC_LOG_DEBUG("network", "WORLD: CMSG_REQUEST_PET_INFO");
|
|
recvData.hexlike();
|
|
*/
|
|
}
|
|
|
|
void WorldSession::HandleSetTaxiBenchmarkOpcode(WorldPacket& recvData)
|
|
{
|
|
TC_LOG_DEBUG("network", "WORLD: CMSG_SET_TAXI_BENCHMARK_MODE");
|
|
|
|
uint8 mode;
|
|
recvData >> mode;
|
|
|
|
mode ? _player->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_TAXI_BENCHMARK) : _player->RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_TAXI_BENCHMARK);
|
|
|
|
TC_LOG_DEBUG("network", "Client used \"/timetest %d\" command", mode);
|
|
}
|
|
|
|
void WorldSession::HandleQueryInspectAchievements(WorldPacket& recvData)
|
|
{
|
|
ObjectGuid guid;
|
|
recvData >> guid.ReadAsPacked();
|
|
|
|
TC_LOG_DEBUG("network", "CMSG_QUERY_INSPECT_ACHIEVEMENTS [%s] Inspected Player [%s]", _player->GetGUID().ToString().c_str(), guid.ToString().c_str());
|
|
Player* player = ObjectAccessor::FindPlayer(guid);
|
|
if (!player)
|
|
return;
|
|
|
|
if (!GetPlayer()->IsWithinDistInMap(player, INSPECT_DISTANCE, false))
|
|
return;
|
|
|
|
if (GetPlayer()->IsValidAttackTarget(player))
|
|
return;
|
|
|
|
player->SendRespondInspectAchievements(_player);
|
|
}
|
|
|
|
void WorldSession::HandleWorldStateUITimerUpdate(WorldPacket& /*recvData*/)
|
|
{
|
|
// empty opcode
|
|
TC_LOG_DEBUG("network", "WORLD: CMSG_WORLD_STATE_UI_TIMER_UPDATE");
|
|
|
|
WorldPacket data(SMSG_WORLD_STATE_UI_TIMER_UPDATE, 4);
|
|
data << uint32(time(NULL));
|
|
SendPacket(&data);
|
|
}
|
|
|
|
void WorldSession::HandleReadyForAccountDataTimes(WorldPacket& /*recvData*/)
|
|
{
|
|
// empty opcode
|
|
TC_LOG_DEBUG("network", "WORLD: CMSG_READY_FOR_ACCOUNT_DATA_TIMES");
|
|
|
|
SendAccountDataTimes(GLOBAL_CACHE_MASK);
|
|
}
|
|
|
|
void WorldSession::SendSetPhaseShift(uint32 PhaseShift)
|
|
{
|
|
WorldPacket data(SMSG_SET_PHASE_SHIFT, 4);
|
|
data << uint32(PhaseShift);
|
|
SendPacket(&data);
|
|
}
|
|
// Battlefield and Battleground
|
|
void WorldSession::HandleAreaSpiritHealerQueryOpcode(WorldPacket& recvData)
|
|
{
|
|
TC_LOG_DEBUG("network", "WORLD: CMSG_AREA_SPIRIT_HEALER_QUERY");
|
|
|
|
Battleground* bg = _player->GetBattleground();
|
|
|
|
ObjectGuid guid;
|
|
recvData >> guid;
|
|
|
|
Creature* unit = GetPlayer()->GetMap()->GetCreature(guid);
|
|
if (!unit)
|
|
return;
|
|
|
|
if (!unit->IsSpiritService()) // it's not spirit service
|
|
return;
|
|
|
|
if (bg)
|
|
sBattlegroundMgr->SendAreaSpiritHealerQueryOpcode(_player, bg, guid);
|
|
|
|
if (Battlefield* bf = sBattlefieldMgr->GetBattlefieldToZoneId(_player->GetZoneId()))
|
|
bf->SendAreaSpiritHealerQueryOpcode(_player, guid);
|
|
}
|
|
|
|
void WorldSession::HandleAreaSpiritHealerQueueOpcode(WorldPacket& recvData)
|
|
{
|
|
TC_LOG_DEBUG("network", "WORLD: CMSG_AREA_SPIRIT_HEALER_QUEUE");
|
|
|
|
Battleground* bg = _player->GetBattleground();
|
|
|
|
ObjectGuid guid;
|
|
recvData >> guid;
|
|
|
|
Creature* unit = GetPlayer()->GetMap()->GetCreature(guid);
|
|
if (!unit)
|
|
return;
|
|
|
|
if (!unit->IsSpiritService()) // it's not spirit service
|
|
return;
|
|
|
|
if (bg)
|
|
bg->AddPlayerToResurrectQueue(guid, _player->GetGUID());
|
|
|
|
if (Battlefield* bf = sBattlefieldMgr->GetBattlefieldToZoneId(_player->GetZoneId()))
|
|
bf->AddPlayerToResurrectQueue(guid, _player->GetGUID());
|
|
}
|
|
|
|
void WorldSession::HandleHearthAndResurrect(WorldPacket& /*recvData*/)
|
|
{
|
|
if (_player->IsInFlight())
|
|
return;
|
|
|
|
if (/*Battlefield* bf = */sBattlefieldMgr->GetBattlefieldToZoneId(_player->GetZoneId()))
|
|
{
|
|
// bf->PlayerAskToLeave(_player); FIXME
|
|
return;
|
|
}
|
|
|
|
AreaTableEntry const* atEntry = sAreaTableStore.LookupEntry(_player->GetAreaId());
|
|
if (!atEntry || !(atEntry->flags & AREA_FLAG_WINTERGRASP_2))
|
|
return;
|
|
|
|
_player->BuildPlayerRepop();
|
|
_player->ResurrectPlayer(1.0f);
|
|
_player->TeleportTo(_player->m_homebindMapId, _player->m_homebindX, _player->m_homebindY, _player->m_homebindZ, _player->GetOrientation());
|
|
}
|
|
|
|
void WorldSession::HandleInstanceLockResponse(WorldPacket& recvPacket)
|
|
{
|
|
uint8 accept;
|
|
recvPacket >> accept;
|
|
|
|
if (!_player->HasPendingBind())
|
|
{
|
|
TC_LOG_INFO("network", "InstanceLockResponse: Player %s (guid %u) tried to bind himself/teleport to graveyard without a pending bind!",
|
|
_player->GetName().c_str(), _player->GetGUID().GetCounter());
|
|
return;
|
|
}
|
|
|
|
if (accept)
|
|
_player->BindToInstance();
|
|
else
|
|
_player->RepopAtGraveyard();
|
|
|
|
_player->SetPendingBind(0, 0);
|
|
}
|
|
|
|
void WorldSession::HandleUpdateMissileTrajectory(WorldPacket& recvPacket)
|
|
{
|
|
TC_LOG_DEBUG("network", "WORLD: CMSG_UPDATE_MISSILE_TRAJECTORY");
|
|
|
|
ObjectGuid guid;
|
|
uint32 spellId;
|
|
float elevation, speed;
|
|
float curX, curY, curZ;
|
|
float targetX, targetY, targetZ;
|
|
uint8 moveStop;
|
|
|
|
recvPacket >> guid >> spellId >> elevation >> speed;
|
|
recvPacket >> curX >> curY >> curZ;
|
|
recvPacket >> targetX >> targetY >> targetZ;
|
|
recvPacket >> moveStop;
|
|
|
|
Unit* caster = ObjectAccessor::GetUnit(*_player, guid);
|
|
Spell* spell = caster ? caster->GetCurrentSpell(CURRENT_GENERIC_SPELL) : NULL;
|
|
if (!spell || spell->m_spellInfo->Id != spellId || !spell->m_targets.HasDst() || !spell->m_targets.HasSrc())
|
|
{
|
|
recvPacket.rfinish();
|
|
return;
|
|
}
|
|
|
|
Position pos = *spell->m_targets.GetSrcPos();
|
|
pos.Relocate(curX, curY, curZ);
|
|
spell->m_targets.ModSrc(pos);
|
|
|
|
pos = *spell->m_targets.GetDstPos();
|
|
pos.Relocate(targetX, targetY, targetZ);
|
|
spell->m_targets.ModDst(pos);
|
|
|
|
spell->m_targets.SetElevation(elevation);
|
|
spell->m_targets.SetSpeed(speed);
|
|
|
|
if (moveStop)
|
|
{
|
|
uint32 opcode;
|
|
recvPacket >> opcode;
|
|
recvPacket.SetOpcode(opcode);
|
|
HandleMovementOpcodes(recvPacket);
|
|
}
|
|
}
|