Core: Change Handlers location

This commit is contained in:
Spp
2012-01-26 10:54:53 +01:00
parent f4b7414dc0
commit c9762b0e38
37 changed files with 5 additions and 3 deletions
+142
View File
@@ -0,0 +1,142 @@
/*
* Copyright (C) 2008-2012 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 "zlib.h"
#include "AddonHandler.h"
#include "DatabaseEnv.h"
#include "Opcodes.h"
#include "Log.h"
AddonHandler::AddonHandler()
{
}
AddonHandler::~AddonHandler()
{
}
bool AddonHandler::BuildAddonPacket(WorldPacket* Source, WorldPacket* Target)
{
ByteBuffer AddOnPacked;
uLongf AddonRealSize;
uint32 CurrentPosition;
uint32 TempValue;
unsigned char tdata[256] =
{
0xC3, 0x5B, 0x50, 0x84, 0xB9, 0x3E, 0x32, 0x42, 0x8C, 0xD0, 0xC7, 0x48, 0xFA, 0x0E, 0x5D, 0x54,
0x5A, 0xA3, 0x0E, 0x14, 0xBA, 0x9E, 0x0D, 0xB9, 0x5D, 0x8B, 0xEE, 0xB6, 0x84, 0x93, 0x45, 0x75,
0xFF, 0x31, 0xFE, 0x2F, 0x64, 0x3F, 0x3D, 0x6D, 0x07, 0xD9, 0x44, 0x9B, 0x40, 0x85, 0x59, 0x34,
0x4E, 0x10, 0xE1, 0xE7, 0x43, 0x69, 0xEF, 0x7C, 0x16, 0xFC, 0xB4, 0xED, 0x1B, 0x95, 0x28, 0xA8,
0x23, 0x76, 0x51, 0x31, 0x57, 0x30, 0x2B, 0x79, 0x08, 0x50, 0x10, 0x1C, 0x4A, 0x1A, 0x2C, 0xC8,
0x8B, 0x8F, 0x05, 0x2D, 0x22, 0x3D, 0xDB, 0x5A, 0x24, 0x7A, 0x0F, 0x13, 0x50, 0x37, 0x8F, 0x5A,
0xCC, 0x9E, 0x04, 0x44, 0x0E, 0x87, 0x01, 0xD4, 0xA3, 0x15, 0x94, 0x16, 0x34, 0xC6, 0xC2, 0xC3,
0xFB, 0x49, 0xFE, 0xE1, 0xF9, 0xDA, 0x8C, 0x50, 0x3C, 0xBE, 0x2C, 0xBB, 0x57, 0xED, 0x46, 0xB9,
0xAD, 0x8B, 0xC6, 0xDF, 0x0E, 0xD6, 0x0F, 0xBE, 0x80, 0xB3, 0x8B, 0x1E, 0x77, 0xCF, 0xAD, 0x22,
0xCF, 0xB7, 0x4B, 0xCF, 0xFB, 0xF0, 0x6B, 0x11, 0x45, 0x2D, 0x7A, 0x81, 0x18, 0xF2, 0x92, 0x7E,
0x98, 0x56, 0x5D, 0x5E, 0x69, 0x72, 0x0A, 0x0D, 0x03, 0x0A, 0x85, 0xA2, 0x85, 0x9C, 0xCB, 0xFB,
0x56, 0x6E, 0x8F, 0x44, 0xBB, 0x8F, 0x02, 0x22, 0x68, 0x63, 0x97, 0xBC, 0x85, 0xBA, 0xA8, 0xF7,
0xB5, 0x40, 0x68, 0x3C, 0x77, 0x86, 0x6F, 0x4B, 0xD7, 0x88, 0xCA, 0x8A, 0xD7, 0xCE, 0x36, 0xF0,
0x45, 0x6E, 0xD5, 0x64, 0x79, 0x0F, 0x17, 0xFC, 0x64, 0xDD, 0x10, 0x6F, 0xF3, 0xF5, 0xE0, 0xA6,
0xC3, 0xFB, 0x1B, 0x8C, 0x29, 0xEF, 0x8E, 0xE5, 0x34, 0xCB, 0xD1, 0x2A, 0xCE, 0x79, 0xC3, 0x9A,
0x0D, 0x36, 0xEA, 0x01, 0xE0, 0xAA, 0x91, 0x20, 0x54, 0xF0, 0x72, 0xD8, 0x1E, 0xC7, 0x89, 0xD2
};
// broken addon packet, can't be received from real client
if (Source->rpos() + 4 > Source->size())
return false;
*Source >> TempValue; // get real size of the packed structure
// empty addon packet, nothing process, can't be received from real client
if (!TempValue)
return false;
AddonRealSize = TempValue; // temp value because ZLIB only excepts uLongf
CurrentPosition = Source->rpos(); // get the position of the pointer in the structure
AddOnPacked.resize(AddonRealSize); // resize target for zlib action
if (!uncompress(const_cast<uint8*>(AddOnPacked.contents()), &AddonRealSize, const_cast<uint8*>((*Source).contents() + CurrentPosition), (*Source).size() - CurrentPosition)!= Z_OK)
{
Target->Initialize(SMSG_ADDON_INFO);
uint32 addonsCount;
AddOnPacked >> addonsCount; // addons count?
for (uint32 i = 0; i < addonsCount; ++i)
{
std::string addonName;
uint8 enabled;
uint32 crc, unk2;
// check next addon data format correctness
if (AddOnPacked.rpos()+1 > AddOnPacked.size())
return false;
AddOnPacked >> addonName;
// recheck next addon data format correctness
if (AddOnPacked.rpos()+1+4+4 > AddOnPacked.size())
return false;
AddOnPacked >> enabled >> crc >> unk2;
sLog->outDebug(LOG_FILTER_NETWORKIO, "ADDON: Name: %s, Enabled: 0x%x, CRC: 0x%x, Unknown2: 0x%x", addonName.c_str(), enabled, crc, unk2);
uint8 state = (enabled ? 2 : 1);
*Target << uint8(state);
uint8 unk1 = (enabled ? 1 : 0);
*Target << uint8(unk1);
if (unk1)
{
uint8 unk = (crc != 0x4c1c776d); // If addon is Standard addon CRC
*Target << uint8(unk);
if (unk)
Target->append(tdata, sizeof(tdata));
*Target << uint32(0);
}
uint8 unk3 = (enabled ? 0 : 1);
*Target << uint8(unk3);
if (unk3)
{
// String, 256 (null terminated?)
*Target << uint8(0);
}
}
uint32 unk4;
AddOnPacked >> unk4;
uint32 count = 0;
*Target << uint32(count);
if (AddOnPacked.rpos() != AddOnPacked.size())
sLog->outDebug(LOG_FILTER_NETWORKIO, "packet under read!");
}
else
{
sLog->outError("Addon packet uncompress error :(");
return false;
}
return true;
}
+40
View File
@@ -0,0 +1,40 @@
/*
* Copyright (C) 2008-2012 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/>.
*/
#ifndef __ADDONHANDLER_H
#define __ADDONHANDLER_H
#include "Common.h"
#include "Config.h"
#include <ace/Singleton.h>
#include "WorldPacket.h"
class AddonHandler
{
/* Construction */
friend class ACE_Singleton<AddonHandler, ACE_Null_Mutex>;
AddonHandler();
public:
~AddonHandler();
//build addon packet
bool BuildAddonPacket(WorldPacket* Source, WorldPacket* Target);
};
#define sAddOnHandler ACE_Singleton<AddonHandler, ACE_Null_Mutex>::instance()
#endif
+408
View File
@@ -0,0 +1,408 @@
/*
* Copyright (C) 2008-2012 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 "Player.h"
#include "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "DatabaseEnv.h"
#include "ArenaTeam.h"
#include "Log.h"
#include "ObjectMgr.h"
#include "SocialMgr.h"
#include "ArenaTeamMgr.h"
void WorldSession::HandleInspectArenaTeamsOpcode(WorldPacket & recvData)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "MSG_INSPECT_ARENA_TEAMS");
uint64 guid;
recvData >> guid;
sLog->outDebug(LOG_FILTER_NETWORKIO, "Inspect Arena stats (GUID: %u TypeId: %u)", GUID_LOPART(guid), GuidHigh2TypeId(GUID_HIPART(guid)));
if (Player* player = ObjectAccessor::FindPlayer(guid))
{
for (uint8 i = 0; i < MAX_ARENA_SLOT; ++i)
{
if (uint32 a_id = player->GetArenaTeamId(i))
{
if (ArenaTeam* arenaTeam = sArenaTeamMgr->GetArenaTeamById(a_id))
arenaTeam->Inspect(this, player->GetGUID());
}
}
}
}
void WorldSession::HandleArenaTeamQueryOpcode(WorldPacket & recvData)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_ARENA_TEAM_QUERY");
uint32 arenaTeamId;
recvData >> arenaTeamId;
if (ArenaTeam* arenaTeam = sArenaTeamMgr->GetArenaTeamById(arenaTeamId))
{
arenaTeam->Query(this);
arenaTeam->SendStats(this);
}
}
void WorldSession::HandleArenaTeamRosterOpcode(WorldPacket & recvData)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_ARENA_TEAM_ROSTER");
uint32 arenaTeamId; // arena team id
recvData >> arenaTeamId;
if (ArenaTeam* arenaTeam = sArenaTeamMgr->GetArenaTeamById(arenaTeamId))
arenaTeam->Roster(this);
}
void WorldSession::HandleArenaTeamInviteOpcode(WorldPacket & recvData)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_ARENA_TEAM_INVITE");
uint32 arenaTeamId; // arena team id
std::string invitedName;
Player* player = NULL;
recvData >> arenaTeamId >> invitedName;
if (!invitedName.empty())
{
if (!normalizePlayerName(invitedName))
return;
player = sObjectAccessor->FindPlayerByName(invitedName.c_str());
}
if (!player)
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", invitedName, ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S);
return;
}
if (player->getLevel() < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", player->GetName(), ERR_ARENA_TEAM_TARGET_TOO_LOW_S);
return;
}
ArenaTeam* arenaTeam = sArenaTeamMgr->GetArenaTeamById(arenaTeamId);
if (!arenaTeam)
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM);
return;
}
// OK result but don't send invite
if (player->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow()))
return;
if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && player->GetTeam() != GetPlayer()->GetTeam())
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED);
return;
}
if (player->GetArenaTeamId(arenaTeam->GetSlot()))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", player->GetName(), ERR_ALREADY_IN_ARENA_TEAM_S);
return;
}
if (player->GetArenaTeamIdInvited())
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", player->GetName(), ERR_ALREADY_INVITED_TO_ARENA_TEAM_S);
return;
}
if (arenaTeam->GetMembersSize() >= arenaTeam->GetType() * 2)
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, arenaTeam->GetName(), "", ERR_ARENA_TEAM_TOO_MANY_MEMBERS_S);
return;
}
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "Player %s Invited %s to Join his ArenaTeam", GetPlayer()->GetName(), invitedName.c_str());
player->SetArenaTeamIdInvited(arenaTeam->GetId());
WorldPacket data(SMSG_ARENA_TEAM_INVITE, (8+10));
data << GetPlayer()->GetName();
data << arenaTeam->GetName();
player->GetSession()->SendPacket(&data);
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_ARENA_TEAM_INVITE");
}
void WorldSession::HandleArenaTeamAcceptOpcode(WorldPacket & /*recv_data*/)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_ARENA_TEAM_ACCEPT"); // empty opcode
ArenaTeam* arenaTeam = sArenaTeamMgr->GetArenaTeamById(_player->GetArenaTeamIdInvited());
if (!arenaTeam)
return;
// Check if player is already in another team of the same size
if (_player->GetArenaTeamId(arenaTeam->GetSlot()))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ALREADY_IN_ARENA_TEAM);
return;
}
// Only allow members of the other faction to join the team if cross faction interaction is enabled
if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && _player->GetTeam() != sObjectMgr->GetPlayerTeamByGUID(arenaTeam->GetCaptain()))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_NOT_ALLIED);
return;
}
// Add player to team
if (!arenaTeam->AddMember(_player->GetGUID()))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_INTERNAL);
return;
}
// Broadcast event
arenaTeam->BroadcastEvent(ERR_ARENA_TEAM_JOIN_SS, _player->GetGUID(), 2, _player->GetName(), arenaTeam->GetName(), "");
}
void WorldSession::HandleArenaTeamDeclineOpcode(WorldPacket & /*recv_data*/)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_ARENA_TEAM_DECLINE"); // empty opcode
// Remove invite from player
_player->SetArenaTeamIdInvited(0);
}
void WorldSession::HandleArenaTeamLeaveOpcode(WorldPacket & recvData)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_ARENA_TEAM_LEAVE");
uint32 arenaTeamId;
recvData >> arenaTeamId;
ArenaTeam* arenaTeam = sArenaTeamMgr->GetArenaTeamById(arenaTeamId);
if (!arenaTeam)
return;
// Disallow leave team while in arena
if (_player->InArena())
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, "", "", ERR_ARENA_TEAM_INTERNAL);
return;
}
// Team captain can't leave the team if other members are still present
if (_player->GetGUID() == arenaTeam->GetCaptain() && arenaTeam->GetMembersSize() > 1)
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, "", "", ERR_ARENA_TEAM_LEADER_LEAVE_S);
return;
}
// If team consists only of the captain, disband the team
if (_player->GetGUID() == arenaTeam->GetCaptain())
{
arenaTeam->Disband(this);
delete arenaTeam;
return;
}
else
arenaTeam->DelMember(_player->GetGUID(), true);
// Broadcast event
arenaTeam->BroadcastEvent(ERR_ARENA_TEAM_LEAVE_SS, _player->GetGUID(), 2, _player->GetName(), arenaTeam->GetName(), "");
// Inform player who left
SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, arenaTeam->GetName(), "", 0);
}
void WorldSession::HandleArenaTeamDisbandOpcode(WorldPacket & recvData)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_ARENA_TEAM_DISBAND");
uint32 arenaTeamId;
recvData >> arenaTeamId;
if (ArenaTeam* arenaTeam = sArenaTeamMgr->GetArenaTeamById(arenaTeamId))
{
// Only captain can disband the team
if (arenaTeam->GetCaptain() != _player->GetGUID())
return;
// Teams cannot be disbanded during fights
if (arenaTeam->IsFighting())
return;
arenaTeam->Disband(this);
delete arenaTeam;
}
}
void WorldSession::HandleArenaTeamRemoveOpcode(WorldPacket & recvData)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_ARENA_TEAM_REMOVE");
uint32 arenaTeamId;
std::string name;
recvData >> arenaTeamId;
recvData >> name;
// Check for valid arena team
ArenaTeam* arenaTeam = sArenaTeamMgr->GetArenaTeamById(arenaTeamId);
if (!arenaTeam)
return;
// Only captain can remove members
if (arenaTeam->GetCaptain() != _player->GetGUID())
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PERMISSIONS);
return;
}
if (!normalizePlayerName(name))
return;
// Check if team member exists
ArenaTeamMember* member = arenaTeam->GetMember(name);
if (!member)
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", name, ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S);
return;
}
// Captain cannot be removed
if (arenaTeam->GetCaptain() == member->Guid)
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, "", "", ERR_ARENA_TEAM_LEADER_LEAVE_S);
return;
}
arenaTeam->DelMember(member->Guid, true);
// Broadcast event
arenaTeam->BroadcastEvent(ERR_ARENA_TEAM_REMOVE_SSS, 0, 3, name, arenaTeam->GetName(), _player->GetName());
}
void WorldSession::HandleArenaTeamLeaderOpcode(WorldPacket & recvData)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_ARENA_TEAM_LEADER");
uint32 arenaTeamId;
std::string name;
recvData >> arenaTeamId;
recvData >> name;
// Check for valid arena team
ArenaTeam* arenaTeam = sArenaTeamMgr->GetArenaTeamById(arenaTeamId);
if (!arenaTeam)
return;
// Only captain can pass leadership
if (arenaTeam->GetCaptain() != _player->GetGUID())
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PERMISSIONS);
return;
}
if (!normalizePlayerName(name))
return;
// Check if team member exists
ArenaTeamMember* member = arenaTeam->GetMember(name);
if (!member)
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", name, ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S);
return;
}
// Check if the target is already team captain
if (arenaTeam->GetCaptain() == member->Guid)
return;
arenaTeam->SetCaptain(member->Guid);
// Broadcast event
arenaTeam->BroadcastEvent(ERR_ARENA_TEAM_LEADER_CHANGED_SSS, 0, 3, _player->GetName(), name, arenaTeam->GetName());
}
void WorldSession::SendArenaTeamCommandResult(uint32 teamAction, const std::string& team, const std::string& player, uint32 errorId)
{
WorldPacket data(SMSG_ARENA_TEAM_COMMAND_RESULT, 4+team.length()+1+player.length()+1+4);
data << uint32(teamAction);
data << team;
data << player;
data << uint32(errorId);
SendPacket(&data);
}
void WorldSession::SendNotInArenaTeamPacket(uint8 type)
{
WorldPacket data(SMSG_ARENA_ERROR, 4+1); // 886 - You are not in a %uv%u arena team
uint32 unk = 0;
data << uint32(unk); // unk(0)
if (!unk)
data << uint8(type); // team type (2=2v2, 3=3v3, 5=5v5), can be used for custom types...
SendPacket(&data);
}
/*
+ERR_ARENA_NO_TEAM_II "You are not in a %dv%d arena team"
+ERR_ARENA_TEAM_CREATE_S "%s created. To disband, use /teamdisband [2v2, 3v3, 5v5]."
+ERR_ARENA_TEAM_INVITE_SS "You have invited %s to join %s"
+ERR_ARENA_TEAM_QUIT_S "You are no longer a member of %s"
ERR_ARENA_TEAM_FOUNDER_S "Congratulations, you are a founding member of %s! To leave, use /teamquit [2v2, 3v3, 5v5]."
+ERR_ARENA_TEAM_INTERNAL "Internal arena team error"
+ERR_ALREADY_IN_ARENA_TEAM "You are already in an arena team of that size"
+ERR_ALREADY_IN_ARENA_TEAM_S "%s is already in an arena team of that size"
+ERR_INVITED_TO_ARENA_TEAM "You have already been invited into an arena team"
+ERR_ALREADY_INVITED_TO_ARENA_TEAM_S "%s has already been invited to an arena team"
+ERR_ARENA_TEAM_NAME_INVALID "That name contains invalid characters, please enter a new name"
+ERR_ARENA_TEAM_NAME_EXISTS_S "There is already an arena team named \"%s\""
+ERR_ARENA_TEAM_LEADER_LEAVE_S "You must promote a new team captain using /teamcaptain before leaving the team"
+ERR_ARENA_TEAM_PERMISSIONS "You don't have permission to do that"
+ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM "You are not in an arena team of that size"
+ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM_SS "%s is not in %s"
+ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S "\"%s\" not found"
+ERR_ARENA_TEAM_NOT_ALLIED "You cannot invite players from the opposing alliance"
+ERR_ARENA_TEAM_JOIN_SS "%s has joined %s"
+ERR_ARENA_TEAM_YOU_JOIN_S "You have joined %s. To leave, use /teamquit [2v2, 3v3, 5v5]."
+ERR_ARENA_TEAM_LEAVE_SS "%s has left %s"
+ERR_ARENA_TEAM_LEADER_IS_SS "%s is the captain of %s"
+ERR_ARENA_TEAM_LEADER_CHANGED_SSS "%s has made %s the new captain of %s"
+ERR_ARENA_TEAM_REMOVE_SSS "%s has been kicked out of %s by %s"
+ERR_ARENA_TEAM_DISBANDED_S "%s has disbanded %s"
ERR_ARENA_TEAM_TARGET_TOO_LOW_S "%s is not high enough level to join your team"
ERR_ARENA_TEAM_TOO_MANY_MEMBERS_S "%s is full"
ERR_ARENA_TEAM_LEVEL_TOO_LOW_I "You must be level %d to form an arena team"
*/
+639
View File
@@ -0,0 +1,639 @@
/*
* Copyright (C) 2008-2012 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 "ObjectMgr.h"
#include "Player.h"
#include "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "AuctionHouseMgr.h"
#include "Log.h"
#include "Opcodes.h"
#include "UpdateMask.h"
#include "Util.h"
#include "AccountMgr.h"
//please DO NOT use iterator++, because it is slower than ++iterator!!!
//post-incrementation is always slower than pre-incrementation !
//void called when player click on auctioneer npc
void WorldSession::HandleAuctionHelloOpcode(WorldPacket & recv_data)
{
uint64 guid; //NPC guid
recv_data >> guid;
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_AUCTIONEER);
if (!unit)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleAuctionHelloOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)));
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
SendAuctionHello(guid, unit);
}
//this void causes that auction window is opened
void WorldSession::SendAuctionHello(uint64 guid, Creature* unit)
{
if (GetPlayer()->getLevel() < sWorld->getIntConfig(CONFIG_AUCTION_LEVEL_REQ))
{
SendNotification(GetTrinityString(LANG_AUCTION_REQ), sWorld->getIntConfig(CONFIG_AUCTION_LEVEL_REQ));
return;
}
AuctionHouseEntry const* ahEntry = AuctionHouseMgr::GetAuctionHouseEntry(unit->getFaction());
if (!ahEntry)
return;
WorldPacket data(MSG_AUCTION_HELLO, 12);
data << uint64(guid);
data << uint32(ahEntry->houseId);
data << uint8(1); // 3.3.3: 1 - AH enabled, 0 - AH disabled
SendPacket(&data);
}
//call this method when player bids, creates, or deletes auction
void WorldSession::SendAuctionCommandResult(uint32 auctionId, uint32 Action, uint32 ErrorCode, uint32 bidError)
{
WorldPacket data(SMSG_AUCTION_COMMAND_RESULT, 16);
data << auctionId;
data << Action;
data << ErrorCode;
if (!ErrorCode && Action)
data << bidError; //when bid, then send 0, once...
SendPacket(&data);
}
//this function sends notification, if bidder is online
void WorldSession::SendAuctionBidderNotification(uint32 location, uint32 auctionId, uint64 bidder, uint32 bidSum, uint32 diff, uint32 item_template)
{
WorldPacket data(SMSG_AUCTION_BIDDER_NOTIFICATION, (8*4));
data << uint32(location);
data << uint32(auctionId);
data << uint64(bidder);
data << uint32(bidSum);
data << uint32(diff);
data << uint32(item_template);
data << uint32(0);
SendPacket(&data);
}
//this void causes on client to display: "Your auction sold"
void WorldSession::SendAuctionOwnerNotification(AuctionEntry* auction)
{
WorldPacket data(SMSG_AUCTION_OWNER_NOTIFICATION, (8*4));
data << uint32(auction->Id);
data << uint32(auction->bid);
data << uint32(0); //unk
data << uint64(0); //unk (bidder guid?)
data << uint32(auction->item_template);
data << uint32(0); //unk
data << float(0); //unk (time?)
SendPacket(&data);
}
//this void creates new auction and adds auction to some auctionhouse
void WorldSession::HandleAuctionSellItem(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_AUCTION_SELL_ITEM");
uint64 auctioneer, item;
uint32 etime, bid, buyout, count;
recv_data >> auctioneer;
recv_data.read_skip<uint32>(); // const 1?
recv_data >> item;
recv_data >> count; // 3.2.2, number of items being auctioned
recv_data >> bid;
recv_data >> buyout;
recv_data >> etime;
Player* player = GetPlayer();
if (!item || !bid || !etime)
return; //check for cheaters
Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(auctioneer, UNIT_NPC_FLAG_AUCTIONEER);
if (!creature)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleAuctionSellItem - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(auctioneer)));
return;
}
AuctionHouseEntry const* auctionHouseEntry = AuctionHouseMgr::GetAuctionHouseEntry(creature->getFaction());
if (!auctionHouseEntry)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleAuctionSellItem - Unit (GUID: %u) has wrong faction.", uint32(GUID_LOPART(auctioneer)));
return;
}
// client send time in minutes, convert to common used sec time
etime *= MINUTE;
// client understand only 3 auction time
switch (etime)
{
case 1*MIN_AUCTION_TIME:
case 2*MIN_AUCTION_TIME:
case 4*MIN_AUCTION_TIME:
break;
default:
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
Item* it = player->GetItemByGuid(item);
//do not allow to sell already auctioned items
if (sAuctionMgr->GetAItem(GUID_LOPART(item)))
{
sLog->outError("AuctionError, player %s is sending item id: %u, but item is already in another auction", player->GetName(), GUID_LOPART(item));
SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR);
return;
}
// prevent sending bag with items (cheat: can be placed in bag after adding equiped empty bag to auction)
if (!it)
{
SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_ITEM_NOT_FOUND);
return;
}
if (!it->CanBeTraded())
{
SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR);
return;
}
if (it->GetTemplate()->Flags & ITEM_PROTO_FLAG_CONJURED || it->GetUInt32Value(ITEM_FIELD_DURATION))
{
SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR);
return;
}
if (it->IsNotEmptyBag())
{
SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR);
return;
}
AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->getFaction());
//we have to take deposit :
uint32 deposit = sAuctionMgr->GetAuctionDeposit(auctionHouseEntry, etime, it, count);
if (!player->HasEnoughMoney(deposit))
{
SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_NOT_ENOUGHT_MONEY);
return;
}
if (AccountMgr::IsGMAccount(GetSecurity()) && sWorld->getBoolConfig(CONFIG_GM_LOG_TRADE))
{
sLog->outCommand(GetAccountId(), "GM %s (Account: %u) create auction: %s (Entry: %u Count: %u)",
GetPlayerName(), GetAccountId(), it->GetTemplate()->Name1.c_str(), it->GetEntry(), count);
}
player->ModifyMoney(-int32(deposit));
uint32 auction_time = uint32(etime * sWorld->getRate(RATE_AUCTION_TIME));
AuctionEntry* AH = new AuctionEntry;
AH->Id = sObjectMgr->GenerateAuctionID();
if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION))
AH->auctioneer = 23442;
else
AH->auctioneer = GUID_LOPART(auctioneer);
AH->item_guidlow = GUID_LOPART(item);
AH->item_template = it->GetEntry();
AH->owner = player->GetGUIDLow();
AH->startbid = bid;
AH->bidder = 0;
AH->bid = 0;
AH->buyout = buyout;
AH->expire_time = time(NULL) + auction_time;
AH->deposit = deposit;
AH->auctionHouseEntry = auctionHouseEntry;
sLog->outDetail("selling item %u to auctioneer %u with initial bid %u with buyout %u and with time %u (in sec) in auctionhouse %u", GUID_LOPART(item), AH->auctioneer, bid, buyout, auction_time, AH->GetHouseId());
sAuctionMgr->AddAItem(it);
auctionHouse->AddAuction(AH);
player->MoveItemFromInventory(it->GetBagSlot(), it->GetSlot(), true);
SQLTransaction trans = CharacterDatabase.BeginTransaction();
it->DeleteFromInventoryDB(trans);
it->SaveToDB(trans); // recursive and not have transaction guard into self, not in inventiory and can be save standalone
AH->SaveToDB(trans);
player->SaveInventoryAndGoldToDB(trans);
CharacterDatabase.CommitTransaction(trans);
SendAuctionCommandResult(AH->Id, AUCTION_SELL_ITEM, AUCTION_OK);
GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION, 1);
}
//this function is called when client bids or buys out auction
void WorldSession::HandleAuctionPlaceBid(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_AUCTION_PLACE_BID");
uint64 auctioneer;
uint32 auctionId;
uint32 price;
recv_data >> auctioneer;
recv_data >> auctionId >> price;
if (!auctionId || !price)
return; //check for cheaters
Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(auctioneer, UNIT_NPC_FLAG_AUCTIONEER);
if (!creature)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleAuctionPlaceBid - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(auctioneer)));
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->getFaction());
AuctionEntry* auction = auctionHouse->GetAuction(auctionId);
Player* player = GetPlayer();
if (!auction || auction->owner == player->GetGUIDLow())
{
//you cannot bid your own auction:
SendAuctionCommandResult(0, AUCTION_PLACE_BID, CANNOT_BID_YOUR_AUCTION_ERROR);
return;
}
// impossible have online own another character (use this for speedup check in case online owner)
Player* auction_owner = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER));
if (!auction_owner && sObjectMgr->GetPlayerAccountIdByGUID(MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER)) == player->GetSession()->GetAccountId())
{
//you cannot bid your another character auction:
SendAuctionCommandResult(0, AUCTION_PLACE_BID, CANNOT_BID_YOUR_AUCTION_ERROR);
return;
}
// cheating
if (price <= auction->bid || price < auction->startbid)
return;
// price too low for next bid if not buyout
if ((price < auction->buyout || auction->buyout == 0) &&
price < auction->bid + auction->GetAuctionOutBid())
{
//auction has already higher bid, client tests it!
return;
}
if (!player->HasEnoughMoney(price))
{
//you don't have enought money!, client tests!
//SendAuctionCommandResult(auction->auctionId, AUCTION_PLACE_BID, ???);
return;
}
SQLTransaction trans = CharacterDatabase.BeginTransaction();
if (price < auction->buyout || auction->buyout == 0)
{
if (auction->bidder > 0)
{
if (auction->bidder == player->GetGUIDLow())
player->ModifyMoney(-int32(price - auction->bid));
else
{
// mail to last bidder and return money
sAuctionMgr->SendAuctionOutbiddedMail(auction, price, GetPlayer(), trans);
player->ModifyMoney(-int32(price));
}
}
else
player->ModifyMoney(-int32(price));
auction->bidder = player->GetGUIDLow();
auction->bid = price;
GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID, price);
trans->PAppend("UPDATE auctionhouse SET buyguid = '%u', lastbid = '%u' WHERE id = '%u'", auction->bidder, auction->bid, auction->Id);
SendAuctionCommandResult(auction->Id, AUCTION_PLACE_BID, AUCTION_OK, 0);
}
else
{
//buyout:
if (player->GetGUIDLow() == auction->bidder)
player->ModifyMoney(-int32(auction->buyout - auction->bid));
else
{
player->ModifyMoney(-int32(auction->buyout));
if (auction->bidder) //buyout for bidded auction ..
sAuctionMgr->SendAuctionOutbiddedMail(auction, auction->buyout, GetPlayer(), trans);
}
auction->bidder = player->GetGUIDLow();
auction->bid = auction->buyout;
GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID, auction->buyout);
//- Mails must be under transaction control too to prevent data loss
sAuctionMgr->SendAuctionSalePendingMail(auction, trans);
sAuctionMgr->SendAuctionSuccessfulMail(auction, trans);
sAuctionMgr->SendAuctionWonMail(auction, trans);
SendAuctionCommandResult(auction->Id, AUCTION_PLACE_BID, AUCTION_OK);
auction->DeleteFromDB(trans);
uint32 item_template = auction->item_template;
sAuctionMgr->RemoveAItem(auction->item_guidlow);
auctionHouse->RemoveAuction(auction, item_template);
}
player->SaveInventoryAndGoldToDB(trans);
CharacterDatabase.CommitTransaction(trans);
}
//this void is called when auction_owner cancels his auction
void WorldSession::HandleAuctionRemoveItem(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_AUCTION_REMOVE_ITEM");
uint64 auctioneer;
uint32 auctionId;
recv_data >> auctioneer;
recv_data >> auctionId;
//sLog->outDebug("Cancel AUCTION AuctionID: %u", auctionId);
Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(auctioneer, UNIT_NPC_FLAG_AUCTIONEER);
if (!creature)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleAuctionRemoveItem - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(auctioneer)));
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->getFaction());
AuctionEntry* auction = auctionHouse->GetAuction(auctionId);
Player* player = GetPlayer();
SQLTransaction trans = CharacterDatabase.BeginTransaction();
if (auction && auction->owner == player->GetGUIDLow())
{
Item* pItem = sAuctionMgr->GetAItem(auction->item_guidlow);
if (pItem)
{
if (auction->bidder > 0) // If we have a bidder, we have to send him the money he paid
{
uint32 auctionCut = auction->GetAuctionCut();
if (!player->HasEnoughMoney(auctionCut)) //player doesn't have enough money, maybe message needed
return;
//some auctionBidderNotification would be needed, but don't know that parts..
sAuctionMgr->SendAuctionCancelledToBidderMail(auction, trans);
player->ModifyMoney(-int32(auctionCut));
}
// Return the item by mail
std::ostringstream msgAuctionCanceledOwner;
msgAuctionCanceledOwner << auction->item_template << ":0:" << AUCTION_CANCELED << ":0:0";
// item will deleted or added to received mail list
MailDraft(msgAuctionCanceledOwner.str(), "") // TODO: fix body
.AddItem(pItem)
.SendMailTo(trans, player, auction, MAIL_CHECK_MASK_COPIED);
}
else
{
sLog->outError("Auction id: %u has non-existed item (item guid : %u)!!!", auction->Id, auction->item_guidlow);
SendAuctionCommandResult(0, AUCTION_CANCEL, AUCTION_INTERNAL_ERROR);
return;
}
}
else
{
SendAuctionCommandResult(0, AUCTION_CANCEL, AUCTION_INTERNAL_ERROR);
//this code isn't possible ... maybe there should be assert
sLog->outError("CHEATER : %u, he tried to cancel auction (id: %u) of another player, or auction is NULL", player->GetGUIDLow(), auctionId);
return;
}
//inform player, that auction is removed
SendAuctionCommandResult(auction->Id, AUCTION_CANCEL, AUCTION_OK);
// Now remove the auction
player->SaveInventoryAndGoldToDB(trans);
auction->DeleteFromDB(trans);
CharacterDatabase.CommitTransaction(trans);
uint32 item_template = auction->item_template;
sAuctionMgr->RemoveAItem(auction->item_guidlow);
auctionHouse->RemoveAuction(auction, item_template);
}
//called when player lists his bids
void WorldSession::HandleAuctionListBidderItems(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_AUCTION_LIST_BIDDER_ITEMS");
uint64 guid; //NPC guid
uint32 listfrom; //page of auctions
uint32 outbiddedCount; //count of outbidded auctions
recv_data >> guid;
recv_data >> listfrom; // not used in fact (this list not have page control in client)
recv_data >> outbiddedCount;
if (recv_data.size() != (16 + outbiddedCount * 4))
{
sLog->outError("Client sent bad opcode!!! with count: %u and size : %lu (must be: %u)", outbiddedCount, (unsigned long)recv_data.size(), (16 + outbiddedCount * 4));
outbiddedCount = 0;
}
Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_AUCTIONEER);
if (!creature)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleAuctionListBidderItems - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)));
recv_data.rfinish();
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->getFaction());
WorldPacket data(SMSG_AUCTION_BIDDER_LIST_RESULT, (4+4+4));
Player* player = GetPlayer();
data << (uint32) 0; //add 0 as count
uint32 count = 0;
uint32 totalcount = 0;
while (outbiddedCount > 0) //add all data, which client requires
{
--outbiddedCount;
uint32 outbiddedAuctionId;
recv_data >> outbiddedAuctionId;
AuctionEntry* auction = auctionHouse->GetAuction(outbiddedAuctionId);
if (auction && auction->BuildAuctionInfo(data))
{
++totalcount;
++count;
}
}
auctionHouse->BuildListBidderItems(data, player, count, totalcount);
data.put<uint32>(0, count); // add count to placeholder
data << totalcount;
data << (uint32)300; //unk 2.3.0
SendPacket(&data);
}
//this void sends player info about his auctions
void WorldSession::HandleAuctionListOwnerItems(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_AUCTION_LIST_OWNER_ITEMS");
uint32 listfrom;
uint64 guid;
recv_data >> guid;
recv_data >> listfrom; // not used in fact (this list not have page control in client)
Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_AUCTIONEER);
if (!creature)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleAuctionListOwnerItems - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)));
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->getFaction());
WorldPacket data(SMSG_AUCTION_OWNER_LIST_RESULT, (4+4+4));
data << (uint32) 0; // amount place holder
uint32 count = 0;
uint32 totalcount = 0;
auctionHouse->BuildListOwnerItems(data, _player, count, totalcount);
data.put<uint32>(0, count);
data << (uint32) totalcount;
data << (uint32) 0;
SendPacket(&data);
}
//this void is called when player clicks on search button
void WorldSession::HandleAuctionListItems(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_AUCTION_LIST_ITEMS");
std::string searchedname;
uint8 levelmin, levelmax, usable;
uint32 listfrom, auctionSlotID, auctionMainCategory, auctionSubCategory, quality;
uint64 guid;
recv_data >> guid;
recv_data >> listfrom; // start, used for page control listing by 50 elements
recv_data >> searchedname;
recv_data >> levelmin >> levelmax;
recv_data >> auctionSlotID >> auctionMainCategory >> auctionSubCategory;
recv_data >> quality >> usable;
recv_data.read_skip<uint8>(); // unk
// this block looks like it uses some lame byte packing or similar...
uint8 unkCnt;
recv_data >> unkCnt;
for (uint8 i = 0; i < unkCnt; i++)
{
recv_data.read_skip<uint8>();
recv_data.read_skip<uint8>();
}
Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_AUCTIONEER);
if (!creature)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleAuctionListItems - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)));
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->getFaction());
//sLog->outDebug("Auctionhouse search (GUID: %u TypeId: %u)",, list from: %u, searchedname: %s, levelmin: %u, levelmax: %u, auctionSlotID: %u, auctionMainCategory: %u, auctionSubCategory: %u, quality: %u, usable: %u",
// GUID_LOPART(guid), GuidHigh2TypeId(GUID_HIPART(guid)), listfrom, searchedname.c_str(), levelmin, levelmax, auctionSlotID, auctionMainCategory, auctionSubCategory, quality, usable);
WorldPacket data(SMSG_AUCTION_LIST_RESULT, (4+4+4));
uint32 count = 0;
uint32 totalcount = 0;
data << (uint32) 0;
// converting string that we try to find to lower case
std::wstring wsearchedname;
if (!Utf8toWStr(searchedname, wsearchedname))
return;
wstrToLower(wsearchedname);
auctionHouse->BuildListAuctionItems(data, _player,
wsearchedname, listfrom, levelmin, levelmax, usable,
auctionSlotID, auctionMainCategory, auctionSubCategory, quality,
count, totalcount);
data.put<uint32>(0, count);
data << (uint32) totalcount;
data << (uint32) 300; // unk 2.3.0 const?
SendPacket(&data);
}
void WorldSession::HandleAuctionListPendingSales(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_AUCTION_LIST_PENDING_SALES");
recv_data.read_skip<uint64>();
uint32 count = 0;
WorldPacket data(SMSG_AUCTION_LIST_PENDING_SALES, 4);
data << uint32(count); // count
/*for (uint32 i = 0; i < count; ++i)
{
data << ""; // string
data << ""; // string
data << uint32(0);
data << uint32(0);
data << float(0);
}*/
SendPacket(&data);
}
+45
View File
@@ -0,0 +1,45 @@
/*
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
*
* 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 "Opcodes.h"
#include "WorldSession.h"
#include "WorldPacket.h"
void WorldSession::SendAuthResponse(uint8 code, bool shortForm, uint32 queuePos)
{
WorldPacket packet(SMSG_AUTH_RESPONSE, 1 + 4 + 1 + 4 + 1 + (shortForm ? 0 : (4 + 1)));
packet << uint8(code);
packet << uint32(0); // BillingTimeRemaining
packet << uint8(0); // BillingPlanFlags
packet << uint32(0); // BillingTimeRested
packet << uint8(Expansion()); // 0 - normal, 1 - TBC, 2 - WOTLK, must be set in database manually for each account
if (!shortForm)
{
packet << uint32(queuePos); // Queue position
packet << uint8(0); // Unk 3.3.0
}
SendPacket(&packet);
}
void WorldSession::SendClientCacheVersion(uint32 version)
{
WorldPacket data(SMSG_CLIENTCACHE_VERSION, 4);
data << uint32(version);
SendPacket(&data);
}
+790
View File
@@ -0,0 +1,790 @@
/*
* Copyright (C) 2008-2012 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 "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "ArenaTeamMgr.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "ArenaTeam.h"
#include "BattlegroundMgr.h"
#include "Battleground.h"
#include "Chat.h"
#include "Language.h"
#include "Log.h"
#include "Player.h"
#include "Object.h"
#include "Opcodes.h"
#include "DisableMgr.h"
#include "Group.h"
void WorldSession::HandleBattlemasterHelloOpcode(WorldPacket & recv_data)
{
uint64 guid;
recv_data >> guid;
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_BATTLEMASTER_HELLO Message from (GUID: %u TypeId:%u)", GUID_LOPART(guid), GuidHigh2TypeId(GUID_HIPART(guid)));
Creature* unit = GetPlayer()->GetMap()->GetCreature(guid);
if (!unit)
return;
if (!unit->isBattleMaster()) // it's not battlemaster
return;
// Stop the npc if moving
unit->StopMoving();
BattlegroundTypeId bgTypeId = sBattlegroundMgr->GetBattleMasterBG(unit->GetEntry());
if (!_player->GetBGAccessByLevel(bgTypeId))
{
// temp, must be gossip message...
SendNotification(LANG_YOUR_BG_LEVEL_REQ_ERROR);
return;
}
SendBattlegGroundList(guid, bgTypeId);
}
void WorldSession::SendBattlegGroundList(uint64 guid, BattlegroundTypeId bgTypeId)
{
WorldPacket data;
sBattlegroundMgr->BuildBattlegroundListPacket(&data, guid, _player, bgTypeId, 0);
SendPacket(&data);
}
void WorldSession::HandleBattlemasterJoinOpcode(WorldPacket & recv_data)
{
uint64 guid;
uint32 bgTypeId_;
uint32 instanceId;
uint8 joinAsGroup;
bool isPremade = false;
Group* grp = NULL;
recv_data >> guid; // battlemaster guid
recv_data >> bgTypeId_; // battleground type id (DBC id)
recv_data >> instanceId; // instance id, 0 if First Available selected
recv_data >> joinAsGroup; // join as group
if (!sBattlemasterListStore.LookupEntry(bgTypeId_))
{
sLog->outError("Battleground: invalid bgtype (%u) received. possible cheater? player guid %u", bgTypeId_, _player->GetGUIDLow());
return;
}
if (DisableMgr::IsDisabledFor(DISABLE_TYPE_BATTLEGROUND, bgTypeId_, NULL))
{
ChatHandler(this).PSendSysMessage(LANG_BG_DISABLED);
return;
}
BattlegroundTypeId bgTypeId = BattlegroundTypeId(bgTypeId_);
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_BATTLEMASTER_JOIN Message from (GUID: %u TypeId:%u)", GUID_LOPART(guid), GuidHigh2TypeId(GUID_HIPART(guid)));
// can do this, since it's battleground, not arena
BattlegroundQueueTypeId bgQueueTypeId = BattlegroundMgr::BGQueueTypeId(bgTypeId, 0);
BattlegroundQueueTypeId bgQueueTypeIdRandom = BattlegroundMgr::BGQueueTypeId(BATTLEGROUND_RB, 0);
// ignore if player is already in BG
if (_player->InBattleground())
return;
// get bg instance or bg template if instance not found
Battleground* bg = NULL;
if (instanceId)
bg = sBattlegroundMgr->GetBattlegroundThroughClientInstance(instanceId, bgTypeId);
if (!bg)
bg = sBattlegroundMgr->GetBattlegroundTemplate(bgTypeId);
if (!bg)
return;
// expected bracket entry
PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketByLevel(bg->GetMapId(), _player->getLevel());
if (!bracketEntry)
return;
GroupJoinBattlegroundResult err;
// check queue conditions
if (!joinAsGroup)
{
if (GetPlayer()->isUsingLfg())
{
// player is using dungeon finder or raid finder
WorldPacket data;
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, ERR_LFG_CANT_USE_BATTLEGROUND);
GetPlayer()->GetSession()->SendPacket(&data);
return;
}
// check Deserter debuff
if (!_player->CanJoinToBattleground())
{
WorldPacket data;
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, ERR_GROUP_JOIN_BATTLEGROUND_DESERTERS);
_player->GetSession()->SendPacket(&data);
return;
}
if (_player->GetBattlegroundQueueIndex(bgQueueTypeIdRandom) < PLAYER_MAX_BATTLEGROUND_QUEUES)
{
//player is already in random queue
WorldPacket data;
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, ERR_IN_RANDOM_BG);
_player->GetSession()->SendPacket(&data);
return;
}
if (_player->InBattlegroundQueue() && bgTypeId == BATTLEGROUND_RB)
{
//player is already in queue, can't start random queue
WorldPacket data;
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, ERR_IN_NON_RANDOM_BG);
_player->GetSession()->SendPacket(&data);
return;
}
// check if already in queue
if (_player->GetBattlegroundQueueIndex(bgQueueTypeId) < PLAYER_MAX_BATTLEGROUND_QUEUES)
//player is already in this queue
return;
// check if has free queue slots
if (!_player->HasFreeBattlegroundQueueId())
{
WorldPacket data;
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, ERR_BATTLEGROUND_TOO_MANY_QUEUES);
_player->GetSession()->SendPacket(&data);
return;
}
BattlegroundQueue& bgQueue = sBattlegroundMgr->m_BattlegroundQueues[bgQueueTypeId];
GroupQueueInfo* ginfo = bgQueue.AddGroup(_player, NULL, bgTypeId, bracketEntry, 0, false, isPremade, 0, 0);
uint32 avgTime = bgQueue.GetAverageQueueWaitTime(ginfo, bracketEntry->GetBracketId());
// already checked if queueSlot is valid, now just get it
uint32 queueSlot = _player->AddBattlegroundQueueId(bgQueueTypeId);
WorldPacket data;
// send status packet (in queue)
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_QUEUE, avgTime, 0, ginfo->ArenaType);
SendPacket(&data);
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "Battleground: player joined queue for bg queue type %u bg type %u: GUID %u, NAME %s", bgQueueTypeId, bgTypeId, _player->GetGUIDLow(), _player->GetName());
}
else
{
grp = _player->GetGroup();
// no group found, error
if (!grp)
return;
if (grp->GetLeaderGUID() != _player->GetGUID())
return;
err = grp->CanJoinBattlegroundQueue(bg, bgQueueTypeId, 0, bg->GetMaxPlayersPerTeam(), false, 0);
isPremade = (grp->GetMembersCount() >= bg->GetMinPlayersPerTeam());
BattlegroundQueue& bgQueue = sBattlegroundMgr->m_BattlegroundQueues[bgQueueTypeId];
GroupQueueInfo* ginfo = NULL;
uint32 avgTime = 0;
if (err > 0)
{
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "Battleground: the following players are joining as group:");
ginfo = bgQueue.AddGroup(_player, grp, bgTypeId, bracketEntry, 0, false, isPremade, 0, 0);
avgTime = bgQueue.GetAverageQueueWaitTime(ginfo, bracketEntry->GetBracketId());
}
for (GroupReference* itr = grp->GetFirstMember(); itr != NULL; itr = itr->next())
{
Player* member = itr->getSource();
if (!member) continue; // this should never happen
WorldPacket data;
if (err <= 0)
{
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, err);
member->GetSession()->SendPacket(&data);
continue;
}
// add to queue
uint32 queueSlot = member->AddBattlegroundQueueId(bgQueueTypeId);
// send status packet (in queue)
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_QUEUE, avgTime, 0, ginfo->ArenaType);
member->GetSession()->SendPacket(&data);
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, err);
member->GetSession()->SendPacket(&data);
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "Battleground: player joined queue for bg queue type %u bg type %u: GUID %u, NAME %s", bgQueueTypeId, bgTypeId, member->GetGUIDLow(), member->GetName());
}
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "Battleground: group end");
}
sBattlegroundMgr->ScheduleQueueUpdate(0, 0, bgQueueTypeId, bgTypeId, bracketEntry->GetBracketId());
}
void WorldSession::HandleBattlegroundPlayerPositionsOpcode(WorldPacket & /*recv_data*/)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd MSG_BATTLEGROUND_PLAYER_POSITIONS Message");
Battleground* bg = _player->GetBattleground();
if (!bg) // can't be received if player not in battleground
return;
uint32 count = 0;
Player* aplr = NULL;
Player* hplr = NULL;
if (uint64 guid = bg->GetFlagPickerGUID(BG_TEAM_ALLIANCE))
{
aplr = ObjectAccessor::FindPlayer(guid);
if (aplr)
++count;
}
if (uint64 guid = bg->GetFlagPickerGUID(BG_TEAM_HORDE))
{
hplr = ObjectAccessor::FindPlayer(guid);
if (hplr)
++count;
}
WorldPacket data(MSG_BATTLEGROUND_PLAYER_POSITIONS, 4 + 4 + 16 * count);
data << 0;
data << count;
if (aplr)
{
data << uint64(aplr->GetGUID());
data << float(aplr->GetPositionX());
data << float(aplr->GetPositionY());
}
if (hplr)
{
data << uint64(hplr->GetGUID());
data << float(hplr->GetPositionX());
data << float(hplr->GetPositionY());
}
SendPacket(&data);
}
void WorldSession::HandlePVPLogDataOpcode(WorldPacket & /*recv_data*/)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd MSG_PVP_LOG_DATA Message");
Battleground* bg = _player->GetBattleground();
if (!bg)
return;
// Prevent players from sending BuildPvpLogDataPacket in an arena except for when sent in BattleGround::EndBattleGround.
if (bg->isArena())
return;
WorldPacket data;
sBattlegroundMgr->BuildPvpLogDataPacket(&data, bg);
SendPacket(&data);
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent MSG_PVP_LOG_DATA Message");
}
void WorldSession::HandleBattlefieldListOpcode(WorldPacket &recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_BATTLEFIELD_LIST Message");
uint32 bgTypeId;
recv_data >> bgTypeId; // id from DBC
uint8 fromWhere;
recv_data >> fromWhere; // 0 - battlemaster (lua: ShowBattlefieldList), 1 - UI (lua: RequestBattlegroundInstanceInfo)
uint8 unk1;
recv_data >> unk1; // Unknown 3.2.2
BattlemasterListEntry const* bl = sBattlemasterListStore.LookupEntry(bgTypeId);
if (!bl)
{
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "BattlegroundHandler: invalid bgtype (%u) with player (Name: %s, GUID: %u) received.", bgTypeId, _player->GetName(), _player->GetGUIDLow());
return;
}
WorldPacket data;
sBattlegroundMgr->BuildBattlegroundListPacket(&data, 0, _player, BattlegroundTypeId(bgTypeId), fromWhere);
SendPacket(&data);
}
void WorldSession::HandleBattleFieldPortOpcode(WorldPacket &recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_BATTLEFIELD_PORT Message");
uint8 type; // arenatype if arena
uint8 unk2; // unk, can be 0x0 (may be if was invited?) and 0x1
uint32 bgTypeId_; // type id from dbc
uint16 unk; // 0x1F90 constant?
uint8 action; // enter battle 0x1, leave queue 0x0
recv_data >> type >> unk2 >> bgTypeId_ >> unk >> action;
if (!sBattlemasterListStore.LookupEntry(bgTypeId_))
{
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "BattlegroundHandler: invalid bgtype (%u) with player (Name: %s, GUID: %u) received.", bgTypeId_, _player->GetName(), _player->GetGUIDLow());
return;
}
if (!_player->InBattlegroundQueue())
{
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "BattlegroundHandler: Invalid CMSG_BATTLEFIELD_PORT received from player (Name: %s, GUID: %u), he is not in bg_queue.", _player->GetName(), _player->GetGUIDLow());
return;
}
//get GroupQueueInfo from BattlegroundQueue
BattlegroundTypeId bgTypeId = BattlegroundTypeId(bgTypeId_);
BattlegroundQueueTypeId bgQueueTypeId = BattlegroundMgr::BGQueueTypeId(bgTypeId, type);
BattlegroundQueue& bgQueue = sBattlegroundMgr->m_BattlegroundQueues[bgQueueTypeId];
//we must use temporary variable, because GroupQueueInfo pointer can be deleted in BattlegroundQueue::RemovePlayer() function
GroupQueueInfo ginfo;
if (!bgQueue.GetPlayerGroupInfoData(_player->GetGUID(), &ginfo))
{
sLog->outError("BattlegroundHandler: itrplayerstatus not found.");
return;
}
// if action == 1, then instanceId is required
if (!ginfo.IsInvitedToBGInstanceGUID && action == 1)
{
sLog->outError("BattlegroundHandler: instance not found.");
return;
}
Battleground* bg = sBattlegroundMgr->GetBattleground(ginfo.IsInvitedToBGInstanceGUID, bgTypeId);
// bg template might and must be used in case of leaving queue, when instance is not created yet
if (!bg && action == 0)
bg = sBattlegroundMgr->GetBattlegroundTemplate(bgTypeId);
if (!bg)
{
sLog->outError("BattlegroundHandler: bg_template not found for type id %u.", bgTypeId);
return;
}
// expected bracket entry
PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketByLevel(bg->GetMapId(), _player->getLevel());
if (!bracketEntry)
return;
//some checks if player isn't cheating - it is not exactly cheating, but we cannot allow it
if (action == 1 && ginfo.ArenaType == 0)
{
//if player is trying to enter battleground (not arena!) and he has deserter debuff, we must just remove him from queue
if (!_player->CanJoinToBattleground())
{
//send bg command result to show nice message
WorldPacket data2;
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data2, ERR_GROUP_JOIN_BATTLEGROUND_DESERTERS);
_player->GetSession()->SendPacket(&data2);
action = 0;
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "Battleground: player %s (%u) has a deserter debuff, do not port him to battleground!", _player->GetName(), _player->GetGUIDLow());
}
//if player don't match battleground max level, then do not allow him to enter! (this might happen when player leveled up during his waiting in queue
if (_player->getLevel() > bg->GetMaxLevel())
{
sLog->outError("Battleground: Player %s (%u) has level (%u) higher than maxlevel (%u) of battleground (%u)! Do not port him to battleground!",
_player->GetName(), _player->GetGUIDLow(), _player->getLevel(), bg->GetMaxLevel(), bg->GetTypeID());
action = 0;
}
}
uint32 queueSlot = _player->GetBattlegroundQueueIndex(bgQueueTypeId);
WorldPacket data;
switch (action)
{
case 1: // port to battleground
if (!_player->IsInvitedForBattlegroundQueueType(bgQueueTypeId))
return; // cheating?
if (!_player->InBattleground())
_player->SetBattlegroundEntryPoint();
// resurrect the player
if (!_player->isAlive())
{
_player->ResurrectPlayer(1.0f);
_player->SpawnCorpseBones();
}
// stop taxi flight at port
if (_player->isInFlight())
{
_player->GetMotionMaster()->MovementExpired();
_player->CleanupAfterTaxiFlight();
}
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, queueSlot, STATUS_IN_PROGRESS, 0, bg->GetStartTime(), bg->GetArenaType());
_player->GetSession()->SendPacket(&data);
// remove battleground queue status from BGmgr
bgQueue.RemovePlayer(_player->GetGUID(), false);
// this is still needed here if battleground "jumping" shouldn't add deserter debuff
// also this is required to prevent stuck at old battleground after SetBattlegroundId set to new
if (Battleground* currentBg = _player->GetBattleground())
currentBg->RemovePlayerAtLeave(_player->GetGUID(), false, true);
// set the destination instance id
_player->SetBattlegroundId(bg->GetInstanceID(), bgTypeId);
// set the destination team
_player->SetBGTeam(ginfo.Team);
// bg->HandleBeforeTeleportToBattleground(_player);
sBattlegroundMgr->SendToBattleground(_player, ginfo.IsInvitedToBGInstanceGUID, bgTypeId);
// add only in HandleMoveWorldPortAck()
// bg->AddPlayer(_player, team);
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "Battleground: player %s (%u) joined battle for bg %u, bgtype %u, queue type %u.", _player->GetName(), _player->GetGUIDLow(), bg->GetInstanceID(), bg->GetTypeID(), bgQueueTypeId);
break;
case 0: // leave queue
// if player leaves rated arena match before match start, it is counted as he played but he lost
if (ginfo.IsRated && ginfo.IsInvitedToBGInstanceGUID)
{
ArenaTeam* at = sArenaTeamMgr->GetArenaTeamById(ginfo.Team);
if (at)
{
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "UPDATING memberLost's personal arena rating for %u by opponents rating: %u, because he has left queue!", GUID_LOPART(_player->GetGUID()), ginfo.OpponentsTeamRating);
at->MemberLost(_player, ginfo.OpponentsMatchmakerRating);
at->SaveToDB();
}
}
_player->RemoveBattlegroundQueueId(bgQueueTypeId); // must be called this way, because if you move this call to queue->removeplayer, it causes bugs
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0, 0);
bgQueue.RemovePlayer(_player->GetGUID(), true);
// player left queue, we should update it - do not update Arena Queue
if (!ginfo.ArenaType)
sBattlegroundMgr->ScheduleQueueUpdate(ginfo.ArenaMatchmakerRating, ginfo.ArenaType, bgQueueTypeId, bgTypeId, bracketEntry->GetBracketId());
SendPacket(&data);
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "Battleground: player %s (%u) left queue for bgtype %u, queue type %u.", _player->GetName(), _player->GetGUIDLow(), bg->GetTypeID(), bgQueueTypeId);
break;
default:
sLog->outError("Battleground port: unknown action %u", action);
break;
}
}
void WorldSession::HandleLeaveBattlefieldOpcode(WorldPacket& recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_LEAVE_BATTLEFIELD Message");
recv_data.read_skip<uint8>(); // unk1
recv_data.read_skip<uint8>(); // unk2
recv_data.read_skip<uint32>(); // BattlegroundTypeId
recv_data.read_skip<uint16>(); // unk3
// not allow leave battleground in combat
if (_player->isInCombat())
if (Battleground* bg = _player->GetBattleground())
if (bg->GetStatus() != STATUS_WAIT_LEAVE)
return;
_player->LeaveBattleground();
}
void WorldSession::HandleBattlefieldStatusOpcode(WorldPacket & /*recv_data*/)
{
// empty opcode
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Battleground status");
WorldPacket data;
// we must update all queues here
Battleground* bg = NULL;
for (uint8 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i)
{
BattlegroundQueueTypeId bgQueueTypeId = _player->GetBattlegroundQueueTypeId(i);
if (!bgQueueTypeId)
continue;
BattlegroundTypeId bgTypeId = BattlegroundMgr::BGTemplateId(bgQueueTypeId);
uint8 arenaType = BattlegroundMgr::BGArenaType(bgQueueTypeId);
if (bgTypeId == _player->GetBattlegroundTypeId())
{
bg = _player->GetBattleground();
//i cannot check any variable from player class because player class doesn't know if player is in 2v2 / 3v3 or 5v5 arena
//so i must use bg pointer to get that information
if (bg && bg->GetArenaType() == arenaType)
{
// this line is checked, i only don't know if GetStartTime is changing itself after bg end!
// send status in Battleground
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, i, STATUS_IN_PROGRESS, bg->GetEndTime(), bg->GetStartTime(), arenaType);
SendPacket(&data);
continue;
}
}
//we are sending update to player about queue - he can be invited there!
//get GroupQueueInfo for queue status
BattlegroundQueue& bgQueue = sBattlegroundMgr->m_BattlegroundQueues[bgQueueTypeId];
GroupQueueInfo ginfo;
if (!bgQueue.GetPlayerGroupInfoData(_player->GetGUID(), &ginfo))
continue;
if (ginfo.IsInvitedToBGInstanceGUID)
{
bg = sBattlegroundMgr->GetBattleground(ginfo.IsInvitedToBGInstanceGUID, bgTypeId);
if (!bg)
continue;
uint32 remainingTime = getMSTimeDiff(getMSTime(), ginfo.RemoveInviteTime);
// send status invited to Battleground
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, i, STATUS_WAIT_JOIN, remainingTime, 0, arenaType);
SendPacket(&data);
}
else
{
bg = sBattlegroundMgr->GetBattlegroundTemplate(bgTypeId);
if (!bg)
continue;
// expected bracket entry
PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketByLevel(bg->GetMapId(), _player->getLevel());
if (!bracketEntry)
continue;
uint32 avgTime = bgQueue.GetAverageQueueWaitTime(&ginfo, bracketEntry->GetBracketId());
// send status in Battleground Queue
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, i, STATUS_WAIT_QUEUE, avgTime, getMSTimeDiff(ginfo.JoinTime, getMSTime()), arenaType);
SendPacket(&data);
}
}
}
void WorldSession::HandleAreaSpiritHealerQueryOpcode(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_AREA_SPIRIT_HEALER_QUERY");
Battleground* bg = _player->GetBattleground();
uint64 guid;
recv_data >> 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);
}
void WorldSession::HandleAreaSpiritHealerQueueOpcode(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_AREA_SPIRIT_HEALER_QUEUE");
Battleground* bg = _player->GetBattleground();
uint64 guid;
recv_data >> 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());
}
void WorldSession::HandleBattlemasterJoinArena(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_BATTLEMASTER_JOIN_ARENA");
uint64 guid; // arena Battlemaster guid
uint8 arenaslot; // 2v2, 3v3 or 5v5
uint8 asGroup; // asGroup
uint8 isRated; // isRated
Group* grp = NULL;
recv_data >> guid >> arenaslot >> asGroup >> isRated;
// ignore if we already in BG or BG queue
if (_player->InBattleground())
return;
Creature* unit = GetPlayer()->GetMap()->GetCreature(guid);
if (!unit)
return;
if (!unit->isBattleMaster()) // it's not battle master
return;
uint8 arenatype = 0;
uint32 arenaRating = 0;
uint32 matchmakerRating = 0;
switch (arenaslot)
{
case 0:
arenatype = ARENA_TYPE_2v2;
break;
case 1:
arenatype = ARENA_TYPE_3v3;
break;
case 2:
arenatype = ARENA_TYPE_5v5;
break;
default:
sLog->outError("Unknown arena slot %u at HandleBattlemasterJoinArena()", arenaslot);
return;
}
//check existance
Battleground* bg = sBattlegroundMgr->GetBattlegroundTemplate(BATTLEGROUND_AA);
if (!bg)
{
sLog->outError("Battleground: template bg (all arenas) not found");
return;
}
if (DisableMgr::IsDisabledFor(DISABLE_TYPE_BATTLEGROUND, BATTLEGROUND_AA, NULL))
{
ChatHandler(this).PSendSysMessage(LANG_ARENA_DISABLED);
return;
}
BattlegroundTypeId bgTypeId = bg->GetTypeID();
BattlegroundQueueTypeId bgQueueTypeId = BattlegroundMgr::BGQueueTypeId(bgTypeId, arenatype);
PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketByLevel(bg->GetMapId(), _player->getLevel());
if (!bracketEntry)
return;
GroupJoinBattlegroundResult err = ERR_GROUP_JOIN_BATTLEGROUND_FAIL;
if (!asGroup)
{
// check if already in queue
if (_player->GetBattlegroundQueueIndex(bgQueueTypeId) < PLAYER_MAX_BATTLEGROUND_QUEUES)
//player is already in this queue
return;
// check if has free queue slots
if (!_player->HasFreeBattlegroundQueueId())
return;
}
else
{
grp = _player->GetGroup();
// no group found, error
if (!grp)
return;
if (grp->GetLeaderGUID() != _player->GetGUID())
return;
err = grp->CanJoinBattlegroundQueue(bg, bgQueueTypeId, arenatype, arenatype, (bool)isRated, arenaslot);
}
uint32 ateamId = 0;
if (isRated)
{
ateamId = _player->GetArenaTeamId(arenaslot);
// check real arenateam existence only here (if it was moved to group->CanJoin .. () then we would ahve to get it twice)
ArenaTeam* at = sArenaTeamMgr->GetArenaTeamById(ateamId);
if (!at)
{
_player->GetSession()->SendNotInArenaTeamPacket(arenatype);
return;
}
// get the team rating for queueing
arenaRating = at->GetRating();
matchmakerRating = at->GetAverageMMR(grp);
// the arenateam id must match for everyone in the group
if (arenaRating <= 0)
arenaRating = 1;
}
BattlegroundQueue &bgQueue = sBattlegroundMgr->m_BattlegroundQueues[bgQueueTypeId];
if (asGroup)
{
uint32 avgTime = 0;
if (err > 0)
{
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "Battleground: arena join as group start");
if (isRated)
{
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "Battleground: arena team id %u, leader %s queued with matchmaker rating %u for type %u", _player->GetArenaTeamId(arenaslot), _player->GetName(), matchmakerRating, arenatype);
bg->SetRated(true);
}
else
bg->SetRated(false);
GroupQueueInfo* ginfo = bgQueue.AddGroup(_player, grp, bgTypeId, bracketEntry, arenatype, isRated, false, arenaRating, matchmakerRating, ateamId);
avgTime = bgQueue.GetAverageQueueWaitTime(ginfo, bracketEntry->GetBracketId());
}
for (GroupReference* itr = grp->GetFirstMember(); itr != NULL; itr = itr->next())
{
Player* member = itr->getSource();
if (!member)
continue;
WorldPacket data;
if (err <= 0)
{
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, err);
member->GetSession()->SendPacket(&data);
continue;
}
// add to queue
uint32 queueSlot = member->AddBattlegroundQueueId(bgQueueTypeId);
// send status packet (in queue)
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_QUEUE, avgTime, 0, arenatype);
member->GetSession()->SendPacket(&data);
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, err);
member->GetSession()->SendPacket(&data);
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "Battleground: player joined queue for arena as group bg queue type %u bg type %u: GUID %u, NAME %s", bgQueueTypeId, bgTypeId, member->GetGUIDLow(), member->GetName());
}
}
else
{
GroupQueueInfo* ginfo = bgQueue.AddGroup(_player, NULL, bgTypeId, bracketEntry, arenatype, isRated, false, arenaRating, matchmakerRating, ateamId);
uint32 avgTime = bgQueue.GetAverageQueueWaitTime(ginfo, bracketEntry->GetBracketId());
uint32 queueSlot = _player->AddBattlegroundQueueId(bgQueueTypeId);
WorldPacket data;
// send status packet (in queue)
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_QUEUE, avgTime, 0, arenatype);
SendPacket(&data);
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "Battleground: player joined queue for arena, skirmish, bg queue type %u bg type %u: GUID %u, NAME %s", bgQueueTypeId, bgTypeId, _player->GetGUIDLow(), _player->GetName());
}
sBattlegroundMgr->ScheduleQueueUpdate(matchmakerRating, arenatype, bgQueueTypeId, bgTypeId, bracketEntry->GetBracketId());
}
void WorldSession::HandleReportPvPAFK(WorldPacket & recv_data)
{
uint64 playerGuid;
recv_data >> playerGuid;
Player* reportedPlayer = ObjectAccessor::FindPlayer(playerGuid);
if (!reportedPlayer)
{
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "WorldSession::HandleReportPvPAFK: player not found");
return;
}
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "WorldSession::HandleReportPvPAFK: %s reported %s", _player->GetName(), reportedPlayer->GetName());
reportedPlayer->ReportedAfkBy(_player);
}
+309
View File
@@ -0,0 +1,309 @@
/*
* Copyright (C) 2008-2012 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 "WorldPacket.h"
#include "WorldSession.h"
#include "InstanceSaveMgr.h"
#include "Log.h"
#include "Opcodes.h"
#include "Player.h"
void WorldSession::HandleCalendarGetCalendar(WorldPacket& /*recv_data*/)
{
uint64 guid = _player->GetGUID();
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_CALENDAR_GET_CALENDAR [" UI64FMTD "]", guid);
time_t cur_time = time_t(time(NULL));
sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_CALENDAR_SEND_CALENDAR [" UI64FMTD "]", guid);
WorldPacket data(SMSG_CALENDAR_SEND_CALENDAR, 4+4*0+4+4*0+4+4);
data << uint32(0); // invite count
/*
for (;;)
{
uint64 inviteId;
uint64 unkGuid0;
uint8 unk1, unk2, unk3;
uint64 creatorGuid;
}
*/
data << uint32(0); // event count
/*
for (;;)
{
uint64 eventId;
std::string title; // 128 chars
uint32 type;
uint32 occurrenceTime;
uint32 flags;
uint32 unk4; -- possibly mapid for dungeon/raid
uint64 creatorGuid;
}
*/
data << uint32(cur_time); // server time
data << uint32(secsToTimeBitFields(cur_time)); // server time
uint32 counter = 0;
size_t p_counter = data.wpos();
data << uint32(counter); // instance save count
for (uint8 i = 0; i < MAX_DIFFICULTY; ++i)
for (Player::BoundInstancesMap::const_iterator itr = _player->m_boundInstances[i].begin(); itr != _player->m_boundInstances[i].end(); ++itr)
if (itr->second.perm)
{
InstanceSave const* save = itr->second.save;
data << uint32(save->GetMapId());
data << uint32(save->GetDifficulty());
data << uint32(save->GetResetTime() - cur_time);
data << uint64(save->GetInstanceId()); // instance save id as unique instance copy id
++counter;
}
data.put<uint32>(p_counter, counter);
data << uint32(1135753200); // unk (28.12.2005 07:00)
counter = 0;
p_counter = data.wpos();
data << uint32(counter); // raid reset count
std::set<uint32> sentMaps;
ResetTimeByMapDifficultyMap const& resets = sInstanceSaveMgr->GetResetTimeMap();
for (ResetTimeByMapDifficultyMap::const_iterator itr = resets.begin(); itr != resets.end(); ++itr)
{
uint32 mapId = PAIR32_LOPART(itr->first);
if (sentMaps.find(mapId) != sentMaps.end())
continue;
MapEntry const* mapEntry = sMapStore.LookupEntry(mapId);
if (!mapEntry || !mapEntry->IsRaid())
continue;
sentMaps.insert(mapId);
data << uint32(mapId);
data << uint32(itr->second - cur_time);
data << uint32(mapEntry->unk_time);
++counter;
}
data.put<uint32>(p_counter, counter);
data << uint32(0); // holiday count?
/*
for (;;)
{
uint32 unk5, unk6, unk7, unk8, unk9;
for (uint32 j = 0; j < 26; ++j)
{
uint32 unk10;
}
for (uint32 j = 0; j < 10; ++j)
{
uint32 unk11;
}
for (uint32 j = 0; j < 10; ++j)
{
uint32 unk12;
}
std::string holidayName; // 64 chars
}
*/
SendPacket(&data);
}
void WorldSession::HandleCalendarGetEvent(WorldPacket& recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CALENDAR_GET_EVENT");
recv_data.read_skip<uint64>(); // unk
}
void WorldSession::HandleCalendarGuildFilter(WorldPacket& recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CALENDAR_GUILD_FILTER");
recv_data.read_skip<uint32>(); // unk1
recv_data.read_skip<uint32>(); // unk2
recv_data.read_skip<uint32>(); // unk3
}
void WorldSession::HandleCalendarArenaTeam(WorldPacket& recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CALENDAR_ARENA_TEAM");
recv_data.read_skip<uint32>(); // unk
}
void WorldSession::HandleCalendarAddEvent(WorldPacket& recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CALENDAR_ADD_EVENT");
recv_data.rfinish(); // set to end to avoid warnings spam
//std::string unk1, unk2;
//recv_data >> (std::string)unk1;
//recv_data >> (std::string)unk2;
//uint8 unk3, unk4;
//uint32 unk5, unk6, unk7, unk8, unk9, count = 0;
//recv_data >> (uint8)unk3;
//recv_data >> (uint8)unk4;
//recv_data >> (uint32)unk5;
//recv_data >> (uint32)unk6;
//recv_data >> (uint32)unk7;
//recv_data >> (uint32)unk8;
//recv_data >> (uint32)unk9;
//if (!((unk9 >> 6) & 1))
//{
// recv_data >> (uint32)count;
// if (count)
// {
// uint8 unk12, unk13;
// uint64 guid;
// for (int i=0; i<count; i++)
// {
// recv_data.readPackGUID(guid);
// recv_data >> (uint8)unk12;
// recv_data >> (uint8)unk13;
// }
// }
//}
}
void WorldSession::HandleCalendarUpdateEvent(WorldPacket& recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CALENDAR_UPDATE_EVENT");
recv_data.rfinish(); // set to end to avoid warnings spam
//recv_data >> uint64
//recv_data >> uint64
//recv_data >> std::string
//recv_data >> std::string
//recv_data >> uint8
//recv_data >> uint8
//recv_data >> uint32
//recv_data >> uint32
//recv_data >> uint32
//recv_data >> uint32
//recv_data >> uint32
}
void WorldSession::HandleCalendarRemoveEvent(WorldPacket& recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CALENDAR_REMOVE_EVENT");
recv_data.rfinish(); // set to end to avoid warnings spam
//recv_data >> uint64
//recv_data >> uint64
//recv_data >> uint32
}
void WorldSession::HandleCalendarCopyEvent(WorldPacket& recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CALENDAR_COPY_EVENT");
recv_data.rfinish(); // set to end to avoid warnings spam
//recv_data >> uint64
//recv_data >> uint64
//recv_data >> uint32
}
void WorldSession::HandleCalendarEventInvite(WorldPacket& recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CALENDAR_EVENT_INVITE");
recv_data.rfinish(); // set to end to avoid warnings spam
//recv_data >> uint64
//recv_data >> uint64
//recv_data >> std::string
//recv_data >> uint8
//recv_data >> uint8
}
void WorldSession::HandleCalendarEventRsvp(WorldPacket& recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CALENDAR_EVENT_RSVP");
recv_data.rfinish(); // set to end to avoid warnings spam
//recv_data >> uint64
//recv_data >> uint64
//recv_data >> uint32
}
void WorldSession::HandleCalendarEventRemoveInvite(WorldPacket& recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CALENDAR_EVENT_REMOVE_INVITE");
recv_data.rfinish(); // set to end to avoid warnings spam
//recv_data.readPackGUID(guid)
//recv_data >> uint64
//recv_data >> uint64
//recv_data >> uint64
}
void WorldSession::HandleCalendarEventStatus(WorldPacket& recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CALENDAR_EVENT_STATUS");
recv_data.rfinish(); // set to end to avoid warnings spam
//recv_data.readPackGUID(guid)
//recv_data >> uint64
//recv_data >> uint64
//recv_data >> uint64
//recv_data >> uint32
}
void WorldSession::HandleCalendarEventModeratorStatus(WorldPacket& recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CALENDAR_EVENT_MODERATOR_STATUS");
recv_data.rfinish(); // set to end to avoid warnings spam
//recv_data.readPackGUID(guid)
//recv_data >> uint64
//recv_data >> uint64
//recv_data >> uint64
//recv_data >> uint32
}
void WorldSession::HandleCalendarComplain(WorldPacket& recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CALENDAR_COMPLAIN");
recv_data.rfinish(); // set to end to avoid warnings spam
//recv_data >> uint64
//recv_data >> uint64
//recv_data >> uint64
}
void WorldSession::HandleCalendarGetNumPending(WorldPacket& /*recv_data*/)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CALENDAR_GET_NUM_PENDING"); // empty
WorldPacket data(SMSG_CALENDAR_SEND_NUM_PENDING, 4);
data << uint32(0); // 0 - no pending invites, 1 - some pending invites
SendPacket(&data);
}
+302
View File
@@ -0,0 +1,302 @@
/*
* Copyright (C) 2008-2012 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 "ObjectMgr.h" // for normalizePlayerName
#include "ChannelMgr.h"
void WorldSession::HandleJoinChannel(WorldPacket& recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
uint32 channel_id;
uint8 unknown1, unknown2;
std::string channelname, pass;
recvPacket >> channel_id;
recvPacket >> unknown1 >> unknown2;
recvPacket >> channelname;
recvPacket >> pass;
if (channel_id)
{
ChatChannelsEntry const* channel = sChatChannelsStore.LookupEntry(channel_id);
if (!channel)
return;
AreaTableEntry const* current_zone = GetAreaEntryByAreaID(_player->GetZoneId());
if (!current_zone)
return;
if (!_player->CanJoinConstantChannelInZone(channel, current_zone))
return;
}
if (channelname.empty())
return;
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
{
cMgr->team = _player->GetTeam();
if (Channel* chn = cMgr->GetJoinChannel(channelname, channel_id))
chn->Join(_player->GetGUID(), pass.c_str());
}
}
void WorldSession::HandleLeaveChannel(WorldPacket& recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
uint32 unk;
std::string channelname;
recvPacket >> unk; // channel id?
recvPacket >> channelname;
if (channelname.empty())
return;
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
{
if (Channel* chn = cMgr->GetChannel(channelname, _player))
chn->Leave(_player->GetGUID(), true);
cMgr->LeftChannel(channelname);
}
}
void WorldSession::HandleChannelList(WorldPacket& recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
std::string channelname;
recvPacket >> channelname;
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
if (Channel* chn = cMgr->GetChannel(channelname, _player))
chn->List(_player);
}
void WorldSession::HandleChannelPassword(WorldPacket& recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
std::string channelname, pass;
recvPacket >> channelname;
recvPacket >> pass;
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
if (Channel* chn = cMgr->GetChannel(channelname, _player))
chn->Password(_player->GetGUID(), pass.c_str());
}
void WorldSession::HandleChannelSetOwner(WorldPacket& recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
std::string channelname, newp;
recvPacket >> channelname;
recvPacket >> newp;
if (!normalizePlayerName(newp))
return;
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
if (Channel* chn = cMgr->GetChannel(channelname, _player))
chn->SetOwner(_player->GetGUID(), newp.c_str());
}
void WorldSession::HandleChannelOwner(WorldPacket& recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
std::string channelname;
recvPacket >> channelname;
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
if (Channel* chn = cMgr->GetChannel(channelname, _player))
chn->SendWhoOwner(_player->GetGUID());
}
void WorldSession::HandleChannelModerator(WorldPacket& recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
std::string channelname, otp;
recvPacket >> channelname;
recvPacket >> otp;
if (!normalizePlayerName(otp))
return;
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
if (Channel* chn = cMgr->GetChannel(channelname, _player))
chn->SetModerator(_player->GetGUID(), otp.c_str());
}
void WorldSession::HandleChannelUnmoderator(WorldPacket& recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
std::string channelname, otp;
recvPacket >> channelname;
recvPacket >> otp;
if (!normalizePlayerName(otp))
return;
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
if (Channel* chn = cMgr->GetChannel(channelname, _player))
chn->UnsetModerator(_player->GetGUID(), otp.c_str());
}
void WorldSession::HandleChannelMute(WorldPacket& recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
std::string channelname, otp;
recvPacket >> channelname;
recvPacket >> otp;
if (!normalizePlayerName(otp))
return;
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
if (Channel* chn = cMgr->GetChannel(channelname, _player))
chn->SetMute(_player->GetGUID(), otp.c_str());
}
void WorldSession::HandleChannelUnmute(WorldPacket& recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
std::string channelname, otp;
recvPacket >> channelname;
recvPacket >> otp;
if (!normalizePlayerName(otp))
return;
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
if (Channel* chn = cMgr->GetChannel(channelname, _player))
chn->UnsetMute(_player->GetGUID(), otp.c_str());
}
void WorldSession::HandleChannelInvite(WorldPacket& recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
std::string channelname, otp;
recvPacket >> channelname;
recvPacket >> otp;
if (!normalizePlayerName(otp))
return;
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
if (Channel* chn = cMgr->GetChannel(channelname, _player))
chn->Invite(_player->GetGUID(), otp.c_str());
}
void WorldSession::HandleChannelKick(WorldPacket& recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
std::string channelname, otp;
recvPacket >> channelname;
recvPacket >> otp;
if (!normalizePlayerName(otp))
return;
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
if (Channel* chn = cMgr->GetChannel(channelname, _player))
chn->Kick(_player->GetGUID(), otp.c_str());
}
void WorldSession::HandleChannelBan(WorldPacket& recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
std::string channelname, otp;
recvPacket >> channelname;
recvPacket >> otp;
if (!normalizePlayerName(otp))
return;
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
if (Channel* chn = cMgr->GetChannel(channelname, _player))
chn->Ban(_player->GetGUID(), otp.c_str());
}
void WorldSession::HandleChannelUnban(WorldPacket& recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
std::string channelname, otp;
recvPacket >> channelname;
recvPacket >> otp;
if (!normalizePlayerName(otp))
return;
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
if (Channel* chn = cMgr->GetChannel(channelname, _player))
chn->UnBan(_player->GetGUID(), otp.c_str());
}
void WorldSession::HandleChannelAnnouncements(WorldPacket& recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
std::string channelname;
recvPacket >> channelname;
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
if (Channel* chn = cMgr->GetChannel(channelname, _player))
chn->Announce(_player->GetGUID());
}
void WorldSession::HandleChannelDisplayListQuery(WorldPacket &recvPacket)
{
// this should be OK because the 2 function _were_ the same
HandleChannelList(recvPacket);
}
void WorldSession::HandleGetChannelMemberCount(WorldPacket &recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
std::string channelname;
recvPacket >> channelname;
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
{
if (Channel* chn = cMgr->GetChannel(channelname, _player))
{
WorldPacket data(SMSG_CHANNEL_MEMBER_COUNT, chn->GetName().size()+1+1+4);
data << chn->GetName();
data << uint8(chn->GetFlags());
data << uint32(chn->GetNumPlayers());
SendPacket(&data);
}
}
}
void WorldSession::HandleSetChannelWatch(WorldPacket &recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
std::string channelname;
recvPacket >> channelname;
/*if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
if (Channel* chn = cMgr->GetChannel(channelname, _player))
chn->JoinNotify(_player->GetGUID());*/
}
File diff suppressed because it is too large Load Diff
+635
View File
@@ -0,0 +1,635 @@
/*
* Copyright (C) 2008-2012 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 "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "GuildMgr.h"
#include "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "DatabaseEnv.h"
#include "CellImpl.h"
#include "Chat.h"
#include "ChannelMgr.h"
#include "GridNotifiersImpl.h"
#include "Group.h"
#include "Guild.h"
#include "Language.h"
#include "Log.h"
#include "Opcodes.h"
#include "Player.h"
#include "SpellAuras.h"
#include "SpellAuraEffects.h"
#include "Util.h"
#include "ScriptMgr.h"
#include "AccountMgr.h"
bool WorldSession::processChatmessageFurtherAfterSecurityChecks(std::string& msg, uint32 lang)
{
if (lang != LANG_ADDON)
{
// strip invisible characters for non-addon messages
if (sWorld->getBoolConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
stripLineInvisibleChars(msg);
if (sWorld->getIntConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY) && AccountMgr::IsPlayerAccount(GetSecurity())
&& !ChatHandler(this).isValidChatMessage(msg.c_str()))
{
sLog->outError("Player %s (GUID: %u) sent a chatmessage with an invalid link: %s", GetPlayer()->GetName(),
GetPlayer()->GetGUIDLow(), msg.c_str());
if (sWorld->getIntConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_KICK))
KickPlayer();
return false;
}
}
return true;
}
void WorldSession::HandleMessagechatOpcode(WorldPacket & recv_data)
{
uint32 type;
uint32 lang;
recv_data >> type;
recv_data >> lang;
if (type >= MAX_CHAT_MSG_TYPE)
{
sLog->outError("CHAT: Wrong message type received: %u", type);
recv_data.rfinish();
return;
}
Player* sender = GetPlayer();
//sLog->outDebug("CHAT: packet received. type %u, lang %u", type, lang);
// prevent talking at unknown language (cheating)
LanguageDesc const* langDesc = GetLanguageDescByID(lang);
if (!langDesc)
{
SendNotification(LANG_UNKNOWN_LANGUAGE);
recv_data.rfinish();
return;
}
if (langDesc->skill_id != 0 && !sender->HasSkill(langDesc->skill_id))
{
// also check SPELL_AURA_COMPREHEND_LANGUAGE (client offers option to speak in that language)
Unit::AuraEffectList const& langAuras = sender->GetAuraEffectsByType(SPELL_AURA_COMPREHEND_LANGUAGE);
bool foundAura = false;
for (Unit::AuraEffectList::const_iterator i = langAuras.begin(); i != langAuras.end(); ++i)
{
if ((*i)->GetMiscValue() == int32(lang))
{
foundAura = true;
break;
}
}
if (!foundAura)
{
SendNotification(LANG_NOT_LEARNED_LANGUAGE);
recv_data.rfinish();
return;
}
}
if (lang == LANG_ADDON)
{
if (sWorld->getBoolConfig(CONFIG_CHATLOG_ADDON))
{
std::string msg = "";
recv_data >> msg;
if (msg.empty())
return;
sScriptMgr->OnPlayerChat(sender, uint32(CHAT_MSG_ADDON), lang, msg);
}
// Disabled addon channel?
if (!sWorld->getBoolConfig(CONFIG_ADDON_CHANNEL))
return;
}
// LANG_ADDON should not be changed nor be affected by flood control
else
{
// send in universal language if player in .gmon mode (ignore spell effects)
if (sender->isGameMaster())
lang = LANG_UNIVERSAL;
else
{
// send in universal language in two side iteration allowed mode
if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT))
lang = LANG_UNIVERSAL;
else
{
switch (type)
{
case CHAT_MSG_PARTY:
case CHAT_MSG_PARTY_LEADER:
case CHAT_MSG_RAID:
case CHAT_MSG_RAID_LEADER:
case CHAT_MSG_RAID_WARNING:
// allow two side chat at group channel if two side group allowed
if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP))
lang = LANG_UNIVERSAL;
break;
case CHAT_MSG_GUILD:
case CHAT_MSG_OFFICER:
// allow two side chat at guild channel if two side guild allowed
if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD))
lang = LANG_UNIVERSAL;
break;
}
}
// but overwrite it by SPELL_AURA_MOD_LANGUAGE auras (only single case used)
Unit::AuraEffectList const& ModLangAuras = sender->GetAuraEffectsByType(SPELL_AURA_MOD_LANGUAGE);
if (!ModLangAuras.empty())
lang = ModLangAuras.front()->GetMiscValue();
}
if (!sender->CanSpeak())
{
std::string timeStr = secsToTimeString(m_muteTime - time(NULL));
SendNotification(GetTrinityString(LANG_WAIT_BEFORE_SPEAKING), timeStr.c_str());
recv_data.rfinish(); // Prevent warnings
return;
}
if (type != CHAT_MSG_AFK && type != CHAT_MSG_DND)
sender->UpdateSpeakTime();
}
if (sender->HasAura(1852) && type != CHAT_MSG_WHISPER)
{
std::string msg="";
recv_data >> msg;
SendNotification(GetTrinityString(LANG_GM_SILENCE), sender->GetName());
return;
}
std::string to, channel, msg;
bool ignoreChecks = false;
switch (type)
{
case CHAT_MSG_SAY:
case CHAT_MSG_EMOTE:
case CHAT_MSG_YELL:
case CHAT_MSG_PARTY:
case CHAT_MSG_PARTY_LEADER:
case CHAT_MSG_GUILD:
case CHAT_MSG_OFFICER:
case CHAT_MSG_RAID:
case CHAT_MSG_RAID_LEADER:
case CHAT_MSG_RAID_WARNING:
case CHAT_MSG_BATTLEGROUND:
case CHAT_MSG_BATTLEGROUND_LEADER:
recv_data >> msg;
break;
case CHAT_MSG_WHISPER:
recv_data >> to;
recv_data >> msg;
break;
case CHAT_MSG_CHANNEL:
recv_data >> channel;
recv_data >> msg;
break;
case CHAT_MSG_AFK:
case CHAT_MSG_DND:
recv_data >> msg;
ignoreChecks = true;
break;
}
if (!ignoreChecks)
{
if (msg.empty())
return;
if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
return;
if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
return;
if (msg.empty())
return;
}
switch (type)
{
case CHAT_MSG_SAY:
case CHAT_MSG_EMOTE:
case CHAT_MSG_YELL:
{
if (sender->getLevel() < sWorld->getIntConfig(CONFIG_CHAT_SAY_LEVEL_REQ))
{
SendNotification(GetTrinityString(LANG_SAY_REQ), sWorld->getIntConfig(CONFIG_CHAT_SAY_LEVEL_REQ));
return;
}
if (type == CHAT_MSG_SAY)
sender->Say(msg, lang);
else if (type == CHAT_MSG_EMOTE)
sender->TextEmote(msg);
else if (type == CHAT_MSG_YELL)
sender->Yell(msg, lang);
} break;
case CHAT_MSG_WHISPER:
{
if (sender->getLevel() < sWorld->getIntConfig(CONFIG_CHAT_WHISPER_LEVEL_REQ))
{
SendNotification(GetTrinityString(LANG_WHISPER_REQ), sWorld->getIntConfig(CONFIG_CHAT_WHISPER_LEVEL_REQ));
return;
}
if (!normalizePlayerName(to))
{
SendPlayerNotFoundNotice(to);
break;
}
Player* receiver = sObjectAccessor->FindPlayerByName(to.c_str());
bool senderIsPlayer = AccountMgr::IsPlayerAccount(GetSecurity());
bool receiverIsPlayer = AccountMgr::IsPlayerAccount(receiver ? receiver->GetSession()->GetSecurity() : SEC_PLAYER);
if (!receiver || (senderIsPlayer && !receiverIsPlayer && !receiver->isAcceptWhispers() && !receiver->IsInWhisperWhiteList(sender->GetGUID())))
{
SendPlayerNotFoundNotice(to);
return;
}
if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT) && senderIsPlayer && receiverIsPlayer)
if (GetPlayer()->GetTeam() != receiver->GetTeam())
{
SendWrongFactionNotice();
return;
}
if (GetPlayer()->HasAura(1852) && !receiver->isGameMaster())
{
SendNotification(GetTrinityString(LANG_GM_SILENCE), GetPlayer()->GetName());
return;
}
// If player is a Gamemaster and doesn't accept whisper, we auto-whitelist every player that the Gamemaster is talking to
if (!senderIsPlayer && !sender->isAcceptWhispers() && !sender->IsInWhisperWhiteList(receiver->GetGUID()))
sender->AddWhisperWhiteList(receiver->GetGUID());
GetPlayer()->Whisper(msg, lang, receiver->GetGUID());
} break;
case CHAT_MSG_PARTY:
case CHAT_MSG_PARTY_LEADER:
{
// if player is in battleground, he cannot say to battleground members by /p
Group* group = GetPlayer()->GetOriginalGroup();
if (!group)
{
group = _player->GetGroup();
if (!group || group->isBGGroup())
return;
}
if (type == CHAT_MSG_PARTY_LEADER && !group->IsLeader(_player->GetGUID()))
return;
sScriptMgr->OnPlayerChat(GetPlayer(), type, lang, msg, group);
WorldPacket data;
ChatHandler::FillMessageData(&data, this, uint8(type), lang, NULL, 0, msg.c_str(), NULL);
group->BroadcastPacket(&data, false, group->GetMemberGroup(GetPlayer()->GetGUID()));
} break;
case CHAT_MSG_GUILD:
{
if (GetPlayer()->GetGuildId())
{
if (Guild* guild = sGuildMgr->GetGuildById(GetPlayer()->GetGuildId()))
{
sScriptMgr->OnPlayerChat(GetPlayer(), type, lang, msg, guild);
guild->BroadcastToGuild(this, false, msg, lang == LANG_ADDON ? LANG_ADDON : LANG_UNIVERSAL);
}
}
} break;
case CHAT_MSG_OFFICER:
{
if (GetPlayer()->GetGuildId())
{
if (Guild* guild = sGuildMgr->GetGuildById(GetPlayer()->GetGuildId()))
{
sScriptMgr->OnPlayerChat(GetPlayer(), type, lang, msg, guild);
guild->BroadcastToGuild(this, true, msg, lang == LANG_ADDON ? LANG_ADDON : LANG_UNIVERSAL);
}
}
} break;
case CHAT_MSG_RAID:
{
// if player is in battleground, he cannot say to battleground members by /ra
Group* group = GetPlayer()->GetOriginalGroup();
if (!group)
{
group = GetPlayer()->GetGroup();
if (!group || group->isBGGroup() || !group->isRaidGroup())
return;
}
sScriptMgr->OnPlayerChat(GetPlayer(), type, lang, msg, group);
WorldPacket data;
ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID, lang, "", 0, msg.c_str(), NULL);
group->BroadcastPacket(&data, false);
} break;
case CHAT_MSG_RAID_LEADER:
{
// if player is in battleground, he cannot say to battleground members by /ra
Group* group = GetPlayer()->GetOriginalGroup();
if (!group)
{
group = GetPlayer()->GetGroup();
if (!group || group->isBGGroup() || !group->isRaidGroup() || !group->IsLeader(_player->GetGUID()))
return;
}
sScriptMgr->OnPlayerChat(GetPlayer(), type, lang, msg, group);
WorldPacket data;
ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID_LEADER, lang, "", 0, msg.c_str(), NULL);
group->BroadcastPacket(&data, false);
} break;
case CHAT_MSG_RAID_WARNING:
{
Group* group = GetPlayer()->GetGroup();
if (!group || !group->isRaidGroup() || !(group->IsLeader(GetPlayer()->GetGUID()) || group->IsAssistant(GetPlayer()->GetGUID())) || group->isBGGroup())
return;
sScriptMgr->OnPlayerChat(GetPlayer(), type, lang, msg, group);
WorldPacket data;
//in battleground, raid warning is sent only to players in battleground - code is ok
ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID_WARNING, lang, "", 0, msg.c_str(), NULL);
group->BroadcastPacket(&data, false);
} break;
case CHAT_MSG_BATTLEGROUND:
{
//battleground raid is always in Player->GetGroup(), never in GetOriginalGroup()
Group* group = GetPlayer()->GetGroup();
if (!group || !group->isBGGroup())
return;
sScriptMgr->OnPlayerChat(GetPlayer(), type, lang, msg, group);
WorldPacket data;
ChatHandler::FillMessageData(&data, this, CHAT_MSG_BATTLEGROUND, lang, "", 0, msg.c_str(), NULL);
group->BroadcastPacket(&data, false);
} break;
case CHAT_MSG_BATTLEGROUND_LEADER:
{
// battleground raid is always in Player->GetGroup(), never in GetOriginalGroup()
Group* group = GetPlayer()->GetGroup();
if (!group || !group->isBGGroup() || !group->IsLeader(GetPlayer()->GetGUID()))
return;
sScriptMgr->OnPlayerChat(GetPlayer(), type, lang, msg, group);
WorldPacket data;
ChatHandler::FillMessageData(&data, this, CHAT_MSG_BATTLEGROUND_LEADER, lang, "", 0, msg.c_str(), NULL);
group->BroadcastPacket(&data, false);
} break;
case CHAT_MSG_CHANNEL:
{
if (AccountMgr::IsPlayerAccount(GetSecurity()))
{
if (_player->getLevel() < sWorld->getIntConfig(CONFIG_CHAT_CHANNEL_LEVEL_REQ))
{
SendNotification(GetTrinityString(LANG_CHANNEL_REQ), sWorld->getIntConfig(CONFIG_CHAT_CHANNEL_LEVEL_REQ));
return;
}
}
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
{
if (Channel* chn = cMgr->GetChannel(channel, _player))
{
sScriptMgr->OnPlayerChat(_player, type, lang, msg, chn);
chn->Say(_player->GetGUID(), msg.c_str(), lang);
}
}
} break;
case CHAT_MSG_AFK:
{
if ((msg.empty() || !_player->isAFK()) && !_player->isInCombat())
{
if (!_player->isAFK())
{
if (msg.empty())
msg = GetTrinityString(LANG_PLAYER_AFK_DEFAULT);
_player->afkMsg = msg;
}
sScriptMgr->OnPlayerChat(_player, type, lang, msg);
_player->ToggleAFK();
if (_player->isAFK() && _player->isDND())
_player->ToggleDND();
}
} break;
case CHAT_MSG_DND:
{
if (msg.empty() || !_player->isDND())
{
if (!_player->isDND())
{
if (msg.empty())
msg = GetTrinityString(LANG_PLAYER_DND_DEFAULT);
_player->dndMsg = msg;
}
sScriptMgr->OnPlayerChat(_player, type, lang, msg);
_player->ToggleDND();
if (_player->isDND() && _player->isAFK())
_player->ToggleAFK();
}
} break;
default:
sLog->outError("CHAT: unknown message type %u, lang: %u", type, lang);
break;
}
}
void WorldSession::HandleEmoteOpcode(WorldPacket & recv_data)
{
if (!GetPlayer()->isAlive() || GetPlayer()->HasUnitState(UNIT_STAT_DIED))
return;
uint32 emote;
recv_data >> emote;
sScriptMgr->OnPlayerEmote(GetPlayer(), emote);
GetPlayer()->HandleEmoteCommand(emote);
}
namespace Trinity
{
class EmoteChatBuilder
{
public:
EmoteChatBuilder(Player const& player, uint32 text_emote, uint32 emote_num, Unit const* target)
: i_player(player), i_text_emote(text_emote), i_emote_num(emote_num), i_target(target) {}
void operator()(WorldPacket& data, LocaleConstant loc_idx)
{
char const* nam = i_target ? i_target->GetNameForLocaleIdx(loc_idx) : NULL;
uint32 namlen = (nam ? strlen(nam) : 0) + 1;
data.Initialize(SMSG_TEXT_EMOTE, (20+namlen));
data << i_player.GetGUID();
data << (uint32)i_text_emote;
data << i_emote_num;
data << (uint32)namlen;
if (namlen > 1)
data.append(nam, namlen);
else
data << (uint8)0x00;
}
private:
Player const& i_player;
uint32 i_text_emote;
uint32 i_emote_num;
Unit const* i_target;
};
} // namespace Trinity
void WorldSession::HandleTextEmoteOpcode(WorldPacket & recv_data)
{
if (!GetPlayer()->isAlive())
return;
if (!GetPlayer()->CanSpeak())
{
std::string timeStr = secsToTimeString(m_muteTime - time(NULL));
SendNotification(GetTrinityString(LANG_WAIT_BEFORE_SPEAKING), timeStr.c_str());
return;
}
uint32 text_emote, emoteNum;
uint64 guid;
recv_data >> text_emote;
recv_data >> emoteNum;
recv_data >> guid;
sScriptMgr->OnPlayerTextEmote(GetPlayer(), text_emote, emoteNum, guid);
EmotesTextEntry const* em = sEmotesTextStore.LookupEntry(text_emote);
if (!em)
return;
uint32 emote_anim = em->textid;
switch (emote_anim)
{
case EMOTE_STATE_SLEEP:
case EMOTE_STATE_SIT:
case EMOTE_STATE_KNEEL:
case EMOTE_ONESHOT_NONE:
break;
default:
// Only allow text-emotes for "dead" entities (feign death included)
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
break;
GetPlayer()->HandleEmoteCommand(emote_anim);
break;
}
Unit* unit = ObjectAccessor::GetUnit(*_player, guid);
CellCoord p = Trinity::ComputeCellCoord(GetPlayer()->GetPositionX(), GetPlayer()->GetPositionY());
Cell cell(p);
cell.SetNoCreate();
Trinity::EmoteChatBuilder emote_builder(*GetPlayer(), text_emote, emoteNum, unit);
Trinity::LocalizedPacketDo<Trinity::EmoteChatBuilder > emote_do(emote_builder);
Trinity::PlayerDistWorker<Trinity::LocalizedPacketDo<Trinity::EmoteChatBuilder > > emote_worker(GetPlayer(), sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE), emote_do);
TypeContainerVisitor<Trinity::PlayerDistWorker<Trinity::LocalizedPacketDo<Trinity::EmoteChatBuilder> >, WorldTypeMapContainer> message(emote_worker);
cell.Visit(p, message, *GetPlayer()->GetMap(), *GetPlayer(), sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE));
GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE, text_emote, 0, unit);
//Send scripted event call
if (unit && unit->GetTypeId() == TYPEID_UNIT && ((Creature*)unit)->AI())
((Creature*)unit)->AI()->ReceiveEmote(GetPlayer(), text_emote);
}
void WorldSession::HandleChatIgnoredOpcode(WorldPacket& recv_data)
{
uint64 iguid;
uint8 unk;
//sLog->outDebug(LOG_FILTER_PACKETIO, "WORLD: Received CMSG_CHAT_IGNORED");
recv_data >> iguid;
recv_data >> unk; // probably related to spam reporting
Player* player = ObjectAccessor::FindPlayer(iguid);
if (!player || !player->GetSession())
return;
WorldPacket data;
ChatHandler::FillMessageData(&data, this, CHAT_MSG_IGNORED, LANG_UNIVERSAL, NULL, GetPlayer()->GetGUID(), GetPlayer()->GetName(), NULL);
player->GetSession()->SendPacket(&data);
}
void WorldSession::HandleChannelDeclineInvite(WorldPacket &recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
}
void WorldSession::SendPlayerNotFoundNotice(std::string name)
{
WorldPacket data(SMSG_CHAT_PLAYER_NOT_FOUND, name.size()+1);
data << name;
SendPacket(&data);
}
void WorldSession::SendPlayerAmbiguousNotice(std::string name)
{
WorldPacket data(SMSG_CHAT_PLAYER_AMBIGUOUS, name.size()+1);
data << name;
SendPacket(&data);
}
void WorldSession::SendWrongFactionNotice()
{
WorldPacket data(SMSG_CHAT_WRONG_FACTION, 0);
SendPacket(&data);
}
void WorldSession::SendChatRestrictedNotice(ChatRestrictionType restriction)
{
WorldPacket data(SMSG_CHAT_RESTRICTED, 1);
data << uint8(restriction);
SendPacket(&data);
}
+97
View File
@@ -0,0 +1,97 @@
/*
* Copyright (C) 2008-2012 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 "Log.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "ObjectAccessor.h"
#include "CreatureAI.h"
#include "ObjectDefines.h"
#include "Vehicle.h"
#include "VehicleDefines.h"
void WorldSession::HandleAttackSwingOpcode(WorldPacket& recv_data)
{
uint64 guid;
recv_data >> guid;
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_ATTACKSWING Message guidlow:%u guidhigh:%u", GUID_LOPART(guid), GUID_HIPART(guid));
Unit* pEnemy = ObjectAccessor::GetUnit(*_player, guid);
if (!pEnemy)
{
// stop attack state at client
SendAttackStop(NULL);
return;
}
if (!_player->IsValidAttackTarget(pEnemy))
{
// stop attack state at client
SendAttackStop(pEnemy);
return;
}
//! Client explicitly checks the following before sending CMSG_ATTACKSWING packet,
//! so we'll place the same check here. Note that it might be possible to reuse this snippet
//! in other places as well.
if (Vehicle* vehicle = _player->GetVehicle())
{
VehicleSeatEntry const* seat = vehicle->GetSeatForPassenger(_player);
ASSERT(seat);
if (!(seat->m_flags & VEHICLE_SEAT_FLAG_CAN_ATTACK))
{
SendAttackStop(pEnemy);
return;
}
}
_player->Attack(pEnemy, true);
}
void WorldSession::HandleAttackStopOpcode(WorldPacket & /*recv_data*/)
{
GetPlayer()->AttackStop();
}
void WorldSession::HandleSetSheathedOpcode(WorldPacket& recv_data)
{
uint32 sheathed;
recv_data >> sheathed;
//sLog->outDebug(LOG_FILTER_PACKETIO, "WORLD: Recvd CMSG_SETSHEATHED Message guidlow:%u value1:%u", GetPlayer()->GetGUIDLow(), sheathed);
if (sheathed >= MAX_SHEATH_STATE)
{
sLog->outError("Unknown sheath state %u ??", sheathed);
return;
}
GetPlayer()->SetSheath(SheathState(sheathed));
}
void WorldSession::SendAttackStop(Unit const* enemy)
{
WorldPacket data(SMSG_ATTACKSTOP, (8+8+4)); // we guess size
data.append(GetPlayer()->GetPackGUID());
data.append(enemy ? enemy->GetPackGUID() : 0); // must be packed guid
data << uint32(0); // unk, can be 1 also
SendPacket(&data);
}
+79
View File
@@ -0,0 +1,79 @@
/*
* Copyright (C) 2008-2012 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 "WorldPacket.h"
#include "WorldSession.h"
#include "Log.h"
#include "Opcodes.h"
#include "UpdateData.h"
#include "Player.h"
void WorldSession::HandleDuelAcceptedOpcode(WorldPacket& recvPacket)
{
uint64 guid;
Player* player;
Player* plTarget;
recvPacket >> guid;
if (!GetPlayer()->duel) // ignore accept from duel-sender
return;
player = GetPlayer();
plTarget = player->duel->opponent;
if (player == player->duel->initiator || !plTarget || player == plTarget || player->duel->startTime != 0 || plTarget->duel->startTime != 0)
return;
//sLog->outDebug(LOG_FILTER_PACKETIO, "WORLD: Received CMSG_DUEL_ACCEPTED");
sLog->outStaticDebug("Player 1 is: %u (%s)", player->GetGUIDLow(), player->GetName());
sLog->outStaticDebug("Player 2 is: %u (%s)", plTarget->GetGUIDLow(), plTarget->GetName());
time_t now = time(NULL);
player->duel->startTimer = now;
plTarget->duel->startTimer = now;
player->SendDuelCountdown(3000);
plTarget->SendDuelCountdown(3000);
}
void WorldSession::HandleDuelCancelledOpcode(WorldPacket& recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_DUEL_CANCELLED");
uint64 guid;
recvPacket >> guid;
// no duel requested
if (!GetPlayer()->duel)
return;
// player surrendered in a duel using /forfeit
if (GetPlayer()->duel->startTime != 0)
{
GetPlayer()->CombatStopWithPets(true);
if (GetPlayer()->duel->opponent)
GetPlayer()->duel->opponent->CombatStopWithPets(true);
GetPlayer()->CastSpell(GetPlayer(), 7267, true); // beg
GetPlayer()->DuelComplete(DUEL_WON);
return;
}
GetPlayer()->DuelComplete(DUEL_INTERRUPTED);
}
+996
View File
@@ -0,0 +1,996 @@
/*
* Copyright (C) 2008-2012 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 "DatabaseEnv.h"
#include "Opcodes.h"
#include "Log.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "World.h"
#include "ObjectMgr.h"
#include "GroupMgr.h"
#include "Player.h"
#include "Group.h"
#include "SocialMgr.h"
#include "Util.h"
#include "SpellAuras.h"
#include "Vehicle.h"
class Aura;
/* differeces from off:
-you can uninvite yourself - is is useful
-you can accept invitation even if leader went offline
*/
/* todo:
-group_destroyed msg is sent but not shown
-reduce xp gaining when in raid group
-quest sharing has to be corrected
-FIX sending PartyMemberStats
*/
void WorldSession::SendPartyResult(PartyOperation operation, const std::string& member, PartyResult res, uint32 val /* = 0 */)
{
WorldPacket data(SMSG_PARTY_COMMAND_RESULT, 4 + member.size() + 1 + 4 + 4);
data << uint32(operation);
data << member;
data << uint32(res);
data << uint32(val); // LFD cooldown related (used with ERR_PARTY_LFG_BOOT_COOLDOWN_S and ERR_PARTY_LFG_BOOT_NOT_ELIGIBLE_S)
SendPacket(&data);
}
void WorldSession::HandleGroupInviteOpcode(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GROUP_INVITE");
std::string membername;
recv_data >> membername;
recv_data.read_skip<uint32>();
// attempt add selected player
// cheating
if (!normalizePlayerName(membername))
{
SendPartyResult(PARTY_OP_INVITE, membername, ERR_BAD_PLAYER_NAME_S);
return;
}
Player* player = sObjectAccessor->FindPlayerByName(membername.c_str());
// no player
if (!player)
{
SendPartyResult(PARTY_OP_INVITE, membername, ERR_BAD_PLAYER_NAME_S);
return;
}
// restrict invite to GMs
if (!sWorld->getBoolConfig(CONFIG_ALLOW_GM_GROUP) && !GetPlayer()->isGameMaster() && player->isGameMaster())
return;
// can't group with
if (!GetPlayer()->isGameMaster() && !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP) && GetPlayer()->GetTeam() != player->GetTeam())
{
SendPartyResult(PARTY_OP_INVITE, membername, ERR_PLAYER_WRONG_FACTION);
return;
}
if (GetPlayer()->GetInstanceId() != 0 && player->GetInstanceId() != 0 && GetPlayer()->GetInstanceId() != player->GetInstanceId() && GetPlayer()->GetMapId() == player->GetMapId())
{
SendPartyResult(PARTY_OP_INVITE, membername, ERR_TARGET_NOT_IN_INSTANCE_S);
return;
}
// just ignore us
if (player->GetInstanceId() != 0 && player->GetDungeonDifficulty() != GetPlayer()->GetDungeonDifficulty())
{
SendPartyResult(PARTY_OP_INVITE, membername, ERR_IGNORING_YOU_S);
return;
}
if (player->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow()))
{
SendPartyResult(PARTY_OP_INVITE, membername, ERR_IGNORING_YOU_S);
return;
}
Group* group = GetPlayer()->GetGroup();
if (group && group->isBGGroup())
group = GetPlayer()->GetOriginalGroup();
Group* group2 = player->GetGroup();
if (group2 && group2->isBGGroup())
group2 = player->GetOriginalGroup();
// player already in another group or invited
if (group2 || player->GetGroupInvite())
{
SendPartyResult(PARTY_OP_INVITE, membername, ERR_ALREADY_IN_GROUP_S);
if (group2)
{
// tell the player that they were invited but it failed as they were already in a group
WorldPacket data(SMSG_GROUP_INVITE, 10); // guess size
data << uint8(0); // invited/already in group flag
data << GetPlayer()->GetName(); // max len 48
data << uint32(0); // unk
data << uint8(0); // count
data << uint32(0); // unk
player->GetSession()->SendPacket(&data);
}
return;
}
if (group)
{
// not have permissions for invite
if (!group->IsLeader(GetPlayer()->GetGUID()) && !group->IsAssistant(GetPlayer()->GetGUID()))
{
SendPartyResult(PARTY_OP_INVITE, "", ERR_NOT_LEADER);
return;
}
// not have place
if (group->IsFull())
{
SendPartyResult(PARTY_OP_INVITE, "", ERR_GROUP_FULL);
return;
}
}
// ok, but group not exist, start a new group
// but don't create and save the group to the DB until
// at least one person joins
if (!group)
{
group = new Group;
// new group: if can't add then delete
if (!group->AddLeaderInvite(GetPlayer()))
{
delete group;
return;
}
if (!group->AddInvite(player))
{
delete group;
return;
}
}
else
{
// already existed group: if can't add then just leave
if (!group->AddInvite(player))
{
return;
}
}
// ok, we do it
WorldPacket data(SMSG_GROUP_INVITE, 10); // guess size
data << uint8(1); // invited/already in group flag
data << GetPlayer()->GetName(); // max len 48
data << uint32(0); // unk
data << uint8(0); // count
data << uint32(0); // unk
player->GetSession()->SendPacket(&data);
SendPartyResult(PARTY_OP_INVITE, membername, ERR_PARTY_RESULT_OK);
}
void WorldSession::HandleGroupAcceptOpcode(WorldPacket& recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GROUP_ACCEPT");
recv_data.read_skip<uint32>();
Group* group = GetPlayer()->GetGroupInvite();
if (!group)
return;
// Remove player from invitees in any case
group->RemoveInvite(GetPlayer());
if (group->GetLeaderGUID() == GetPlayer()->GetGUID())
{
sLog->outError("HandleGroupAcceptOpcode: player %s(%d) tried to accept an invite to his own group", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow());
return;
}
// Group is full
if (group->IsFull())
{
SendPartyResult(PARTY_OP_INVITE, "", ERR_GROUP_FULL);
return;
}
Player* leader = ObjectAccessor::FindPlayer(group->GetLeaderGUID());
// Forming a new group, create it
if (!group->IsCreated())
{
// This can happen if the leader is zoning. To be removed once delayed actions for zoning are implemented
if (!leader)
{
group->RemoveAllInvites();
return;
}
// If we're about to create a group there really should be a leader present
ASSERT(leader);
group->RemoveInvite(leader);
group->Create(leader);
sGroupMgr->AddGroup(group);
}
// Everything is fine, do it, PLAYER'S GROUP IS SET IN ADDMEMBER!!!
if (!group->AddMember(GetPlayer()))
return;
group->BroadcastGroupUpdate();
}
void WorldSession::HandleGroupDeclineOpcode(WorldPacket & /*recv_data*/)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GROUP_DECLINE");
Group *group = GetPlayer()->GetGroupInvite();
if (!group) return;
// Remember leader if online (group pointer will be invalid if group gets disbanded)
Player* leader = ObjectAccessor::FindPlayer(group->GetLeaderGUID());
// uninvite, group can be deleted
GetPlayer()->UninviteFromGroup();
if (!leader || !leader->GetSession())
return;
// report
std::string name = std::string(GetPlayer()->GetName());
WorldPacket data(SMSG_GROUP_DECLINE, name.length());
data << name.c_str();
leader->GetSession()->SendPacket(&data);
}
void WorldSession::HandleGroupUninviteGuidOpcode(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GROUP_UNINVITE_GUID");
uint64 guid;
std::string reason;
recv_data >> guid;
recv_data >> reason;
//can't uninvite yourself
if (guid == GetPlayer()->GetGUID())
{
sLog->outError("WorldSession::HandleGroupUninviteGuidOpcode: leader %s(%d) tried to uninvite himself from the group.", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow());
return;
}
PartyResult res = GetPlayer()->CanUninviteFromGroup();
if (res != ERR_PARTY_RESULT_OK)
{
SendPartyResult(PARTY_OP_UNINVITE, "", res);
return;
}
Group* grp = GetPlayer()->GetGroup();
if (!grp)
return;
if (grp->IsLeader(guid))
{
SendPartyResult(PARTY_OP_UNINVITE, "", ERR_NOT_LEADER);
return;
}
if (grp->IsMember(guid))
{
Player::RemoveFromGroup(grp, guid, GROUP_REMOVEMETHOD_KICK, GetPlayer()->GetGUID(), reason.c_str());
return;
}
if (Player* player = grp->GetInvited(guid))
{
player->UninviteFromGroup();
return;
}
SendPartyResult(PARTY_OP_UNINVITE, "", ERR_TARGET_NOT_IN_GROUP_S);
}
void WorldSession::HandleGroupUninviteOpcode(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GROUP_UNINVITE");
std::string membername;
recv_data >> membername;
// player not found
if (!normalizePlayerName(membername))
return;
// can't uninvite yourself
if (GetPlayer()->GetName() == membername)
{
sLog->outError("WorldSession::HandleGroupUninviteOpcode: leader %s(%d) tried to uninvite himself from the group.", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow());
return;
}
PartyResult res = GetPlayer()->CanUninviteFromGroup();
if (res != ERR_PARTY_RESULT_OK)
{
SendPartyResult(PARTY_OP_UNINVITE, "", res);
return;
}
Group* grp = GetPlayer()->GetGroup();
if (!grp)
return;
if (uint64 guid = grp->GetMemberGUID(membername))
{
Player::RemoveFromGroup(grp, guid, GROUP_REMOVEMETHOD_KICK, GetPlayer()->GetGUID());
return;
}
if (Player* player = grp->GetInvited(membername))
{
player->UninviteFromGroup();
return;
}
SendPartyResult(PARTY_OP_UNINVITE, membername, ERR_TARGET_NOT_IN_GROUP_S);
}
void WorldSession::HandleGroupSetLeaderOpcode(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GROUP_SET_LEADER");
uint64 guid;
recv_data >> guid;
Player* player = ObjectAccessor::FindPlayer(guid);
Group* group = GetPlayer()->GetGroup();
if (!group || !player)
return;
if (!group->IsLeader(GetPlayer()->GetGUID()) || player->GetGroup() != group)
return;
// Everything's fine, accepted.
group->ChangeLeader(guid);
group->SendUpdate();
}
void WorldSession::HandleGroupDisbandOpcode(WorldPacket & /*recv_data*/)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GROUP_DISBAND");
Group* grp = GetPlayer()->GetGroup();
if (!grp)
return;
if (_player->InBattleground())
{
SendPartyResult(PARTY_OP_INVITE, "", ERR_INVITE_RESTRICTED);
return;
}
/** error handling **/
/********************/
// everything's fine, do it
SendPartyResult(PARTY_OP_LEAVE, GetPlayer()->GetName(), ERR_PARTY_RESULT_OK);
GetPlayer()->RemoveFromGroup(GROUP_REMOVEMETHOD_LEAVE);
}
void WorldSession::HandleLootMethodOpcode(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_LOOT_METHOD");
uint32 lootMethod;
uint64 lootMaster;
uint32 lootThreshold;
recv_data >> lootMethod >> lootMaster >> lootThreshold;
Group* group = GetPlayer()->GetGroup();
if (!group)
return;
/** error handling **/
if (!group->IsLeader(GetPlayer()->GetGUID()))
return;
/********************/
// everything's fine, do it
group->SetLootMethod((LootMethod)lootMethod);
group->SetLooterGuid(lootMaster);
group->SetLootThreshold((ItemQualities)lootThreshold);
group->SendUpdate();
}
void WorldSession::HandleLootRoll(WorldPacket &recv_data)
{
if (!GetPlayer()->GetGroup())
{
recv_data.rfinish();
return;
}
uint64 Guid;
uint32 NumberOfPlayers;
uint8 rollType;
recv_data >> Guid; //guid of the item rolled
recv_data >> NumberOfPlayers;
recv_data >> rollType; //0: pass, 1: need, 2: greed
//sLog->outDebug("WORLD RECIEVE CMSG_LOOT_ROLL, From:%u, Numberofplayers:%u, Choise:%u", (uint32)Guid, NumberOfPlayers, Choise);
Group* group = GetPlayer()->GetGroup();
if (!group)
return;
// everything's fine, do it
group->CountRollVote(GetPlayer()->GetGUID(), Guid, NumberOfPlayers, rollType);
switch (rollType)
{
case ROLL_NEED:
GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED, 1);
break;
case ROLL_GREED:
GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED, 1);
break;
}
}
void WorldSession::HandleMinimapPingOpcode(WorldPacket& recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received MSG_MINIMAP_PING");
if (!GetPlayer()->GetGroup())
return;
float x, y;
recv_data >> x;
recv_data >> y;
//sLog->outDebug("Received opcode MSG_MINIMAP_PING X: %f, Y: %f", x, y);
/** error handling **/
/********************/
// everything's fine, do it
WorldPacket data(MSG_MINIMAP_PING, (8+4+4));
data << uint64(GetPlayer()->GetGUID());
data << float(x);
data << float(y);
GetPlayer()->GetGroup()->BroadcastPacket(&data, true, -1, GetPlayer()->GetGUID());
}
void WorldSession::HandleRandomRollOpcode(WorldPacket& recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received MSG_RANDOM_ROLL");
uint32 minimum, maximum, roll;
recv_data >> minimum;
recv_data >> maximum;
/** error handling **/
if (minimum > maximum || maximum > 10000) // < 32768 for urand call
return;
/********************/
// everything's fine, do it
roll = urand(minimum, maximum);
//sLog->outDebug("ROLL: MIN: %u, MAX: %u, ROLL: %u", minimum, maximum, roll);
WorldPacket data(MSG_RANDOM_ROLL, 4+4+4+8);
data << uint32(minimum);
data << uint32(maximum);
data << uint32(roll);
data << uint64(GetPlayer()->GetGUID());
if (GetPlayer()->GetGroup())
GetPlayer()->GetGroup()->BroadcastPacket(&data, false);
else
SendPacket(&data);
}
void WorldSession::HandleRaidTargetUpdateOpcode(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received MSG_RAID_TARGET_UPDATE");
Group* group = GetPlayer()->GetGroup();
if (!group)
return;
uint8 x;
recv_data >> x;
/** error handling **/
/********************/
// everything's fine, do it
if (x == 0xFF) // target icon request
{
group->SendTargetIconList(this);
}
else // target icon update
{
if (!group->IsLeader(GetPlayer()->GetGUID()) && !group->IsAssistant(GetPlayer()->GetGUID()))
return;
uint64 guid;
recv_data >> guid;
group->SetTargetIcon(x, _player->GetGUID(), guid);
}
}
void WorldSession::HandleGroupRaidConvertOpcode(WorldPacket & /*recv_data*/)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GROUP_RAID_CONVERT");
Group* group = GetPlayer()->GetGroup();
if (!group)
return;
if (_player->InBattleground())
return;
/** error handling **/
if (!group->IsLeader(GetPlayer()->GetGUID()) || group->GetMembersCount() < 2)
return;
/********************/
// everything's fine, do it (is it 0 (PARTY_OP_INVITE) correct code)
SendPartyResult(PARTY_OP_INVITE, "", ERR_PARTY_RESULT_OK);
group->ConvertToRaid();
}
void WorldSession::HandleGroupChangeSubGroupOpcode(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GROUP_CHANGE_SUB_GROUP");
// we will get correct pointer for group here, so we don't have to check if group is BG raid
Group* group = GetPlayer()->GetGroup();
if (!group)
return;
std::string name;
uint8 groupNr;
recv_data >> name;
recv_data >> groupNr;
if (groupNr >= MAX_RAID_SUBGROUPS)
return;
uint64 senderGuid = GetPlayer()->GetGUID();
if (!group->IsLeader(senderGuid) && !group->IsAssistant(senderGuid))
return;
if (!group->HasFreeSlotSubGroup(groupNr))
return;
Player* movedPlayer = sObjectAccessor->FindPlayerByName(name.c_str());
uint64 guid;
if (movedPlayer)
{
guid = movedPlayer->GetGUID();
}
else
{
CharacterDatabase.EscapeString(name);
guid = sObjectMgr->GetPlayerGUIDByName(name.c_str());
}
group->ChangeMembersGroup(guid, groupNr);
}
void WorldSession::HandleGroupAssistantLeaderOpcode(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GROUP_ASSISTANT_LEADER");
Group* group = GetPlayer()->GetGroup();
if (!group)
return;
if (!group->IsLeader(GetPlayer()->GetGUID()))
return;
uint64 guid;
bool apply;
recv_data >> guid;
recv_data >> apply;
group->SetGroupMemberFlag(guid, apply, MEMBER_FLAG_ASSISTANT);
group->SendUpdate();
}
void WorldSession::HandlePartyAssignmentOpcode(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received MSG_PARTY_ASSIGNMENT");
Group* group = GetPlayer()->GetGroup();
if (!group)
return;
uint64 senderGuid = GetPlayer()->GetGUID();
if (!group->IsLeader(senderGuid) && !group->IsAssistant(senderGuid))
return;
uint8 assignment;
bool apply;
uint64 guid;
recv_data >> assignment >> apply;
recv_data >> guid;
switch (assignment)
{
case GROUP_ASSIGN_MAINASSIST:
group->RemoveUniqueGroupMemberFlag(MEMBER_FLAG_MAINASSIST);
group->SetGroupMemberFlag(guid, apply, MEMBER_FLAG_MAINASSIST);
break;
case GROUP_ASSIGN_MAINTANK:
group->RemoveUniqueGroupMemberFlag(MEMBER_FLAG_MAINTANK); // Remove main assist flag from current if any.
group->SetGroupMemberFlag(guid, apply, MEMBER_FLAG_MAINTANK);
default:
break;
}
group->SendUpdate();
}
void WorldSession::HandleRaidReadyCheckOpcode(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received MSG_RAID_READY_CHECK");
Group* group = GetPlayer()->GetGroup();
if (!group)
return;
if (recv_data.empty()) // request
{
/** error handling **/
if (!group->IsLeader(GetPlayer()->GetGUID()) && !group->IsAssistant(GetPlayer()->GetGUID()))
return;
/********************/
// everything's fine, do it
WorldPacket data(MSG_RAID_READY_CHECK, 8);
data << GetPlayer()->GetGUID();
group->BroadcastPacket(&data, false, -1);
group->OfflineReadyCheck();
}
else // answer
{
uint8 state;
recv_data >> state;
// everything's fine, do it
WorldPacket data(MSG_RAID_READY_CHECK_CONFIRM, 9);
data << uint64(GetPlayer()->GetGUID());
data << uint8(state);
group->BroadcastReadyCheck(&data);
}
}
void WorldSession::HandleRaidReadyCheckFinishedOpcode(WorldPacket & /*recv_data*/)
{
//Group* group = GetPlayer()->GetGroup();
//if (!group)
// return;
//if (!group->IsLeader(GetPlayer()->GetGUID()) && !group->IsAssistant(GetPlayer()->GetGUID()))
// return;
// Is any reaction need?
}
void WorldSession::BuildPartyMemberStatsChangedPacket(Player* player, WorldPacket* data)
{
uint32 mask = player->GetGroupUpdateFlag();
if (mask == GROUP_UPDATE_FLAG_NONE)
return;
if (mask & GROUP_UPDATE_FLAG_POWER_TYPE) // if update power type, update current/max power also
mask |= (GROUP_UPDATE_FLAG_CUR_POWER | GROUP_UPDATE_FLAG_MAX_POWER);
if (mask & GROUP_UPDATE_FLAG_PET_POWER_TYPE) // same for pets
mask |= (GROUP_UPDATE_FLAG_PET_CUR_POWER | GROUP_UPDATE_FLAG_PET_MAX_POWER);
uint32 byteCount = 0;
for (int i = 1; i < GROUP_UPDATE_FLAGS_COUNT; ++i)
if (mask & (1 << i))
byteCount += GroupUpdateLength[i];
data->Initialize(SMSG_PARTY_MEMBER_STATS, 8 + 4 + byteCount);
data->append(player->GetPackGUID());
*data << (uint32) mask;
if (mask & GROUP_UPDATE_FLAG_STATUS)
{
if (player)
{
if (player->IsPvP())
*data << (uint16) (MEMBER_STATUS_ONLINE | MEMBER_STATUS_PVP);
else
*data << (uint16) MEMBER_STATUS_ONLINE;
}
else
*data << (uint16) MEMBER_STATUS_OFFLINE;
}
if (mask & GROUP_UPDATE_FLAG_CUR_HP)
*data << (uint32) player->GetHealth();
if (mask & GROUP_UPDATE_FLAG_MAX_HP)
*data << (uint32) player->GetMaxHealth();
Powers powerType = player->getPowerType();
if (mask & GROUP_UPDATE_FLAG_POWER_TYPE)
*data << (uint8) powerType;
if (mask & GROUP_UPDATE_FLAG_CUR_POWER)
*data << (uint16) player->GetPower(powerType);
if (mask & GROUP_UPDATE_FLAG_MAX_POWER)
*data << (uint16) player->GetMaxPower(powerType);
if (mask & GROUP_UPDATE_FLAG_LEVEL)
*data << (uint16) player->getLevel();
if (mask & GROUP_UPDATE_FLAG_ZONE)
*data << (uint16) player->GetZoneId();
if (mask & GROUP_UPDATE_FLAG_POSITION)
*data << (uint16) player->GetPositionX() << (uint16) player->GetPositionY();
if (mask & GROUP_UPDATE_FLAG_AURAS)
{
uint64 auramask = player->GetAuraUpdateMaskForRaid();
*data << uint64(auramask);
for (uint32 i = 0; i < MAX_AURAS; ++i)
{
if (auramask & (uint64(1) << i))
{
AuraApplication const* aurApp = player->GetVisibleAura(i);
*data << uint32(aurApp ? aurApp->GetBase()->GetId() : 0);
*data << uint8(1);
}
}
}
Pet* pet = player->GetPet();
if (mask & GROUP_UPDATE_FLAG_PET_GUID)
{
if (pet)
*data << (uint64) pet->GetGUID();
else
*data << (uint64) 0;
}
if (mask & GROUP_UPDATE_FLAG_PET_NAME)
{
if (pet)
*data << pet->GetName();
else
*data << (uint8) 0;
}
if (mask & GROUP_UPDATE_FLAG_PET_MODEL_ID)
{
if (pet)
*data << (uint16) pet->GetDisplayId();
else
*data << (uint16) 0;
}
if (mask & GROUP_UPDATE_FLAG_PET_CUR_HP)
{
if (pet)
*data << (uint32) pet->GetHealth();
else
*data << (uint32) 0;
}
if (mask & GROUP_UPDATE_FLAG_PET_MAX_HP)
{
if (pet)
*data << (uint32) pet->GetMaxHealth();
else
*data << (uint32) 0;
}
if (mask & GROUP_UPDATE_FLAG_PET_POWER_TYPE)
{
if (pet)
*data << (uint8) pet->getPowerType();
else
*data << (uint8) 0;
}
if (mask & GROUP_UPDATE_FLAG_PET_CUR_POWER)
{
if (pet)
*data << (uint16) pet->GetPower(pet->getPowerType());
else
*data << (uint16) 0;
}
if (mask & GROUP_UPDATE_FLAG_PET_MAX_POWER)
{
if (pet)
*data << (uint16) pet->GetMaxPower(pet->getPowerType());
else
*data << (uint16) 0;
}
if (mask & GROUP_UPDATE_FLAG_VEHICLE_SEAT)
{
if (Vehicle* veh = player->GetVehicle())
*data << (uint32) veh->GetVehicleInfo()->m_seatID[player->m_movementInfo.t_seat];
}
if (mask & GROUP_UPDATE_FLAG_PET_AURAS)
{
if (pet)
{
uint64 auramask = pet->GetAuraUpdateMaskForRaid();
*data << uint64(auramask);
for (uint32 i = 0; i < MAX_AURAS; ++i)
{
if (auramask & (uint64(1) << i))
{
AuraApplication const* aurApp = player->GetVisibleAura(i);
*data << uint32(aurApp ? aurApp->GetBase()->GetId() : 0);
*data << uint8(1);
}
}
}
else
*data << (uint64) 0;
}
}
/*this procedure handles clients CMSG_REQUEST_PARTY_MEMBER_STATS request*/
void WorldSession::HandleRequestPartyMemberStatsOpcode(WorldPacket &recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_REQUEST_PARTY_MEMBER_STATS");
uint64 Guid;
recv_data >> Guid;
Player* player = HashMapHolder<Player>::Find(Guid);
if (!player)
{
WorldPacket data(SMSG_PARTY_MEMBER_STATS_FULL, 3+4+2);
data << uint8(0); // only for SMSG_PARTY_MEMBER_STATS_FULL, probably arena/bg related
data.appendPackGUID(Guid);
data << (uint32) GROUP_UPDATE_FLAG_STATUS;
data << (uint16) MEMBER_STATUS_OFFLINE;
SendPacket(&data);
return;
}
Pet* pet = player->GetPet();
WorldPacket data(SMSG_PARTY_MEMBER_STATS_FULL, 4+2+2+2+1+2*6+8+1+8);
data << uint8(0); // only for SMSG_PARTY_MEMBER_STATS_FULL, probably arena/bg related
data.append(player->GetPackGUID());
uint32 mask1 = 0x00040BFF; // common mask, real flags used 0x000040BFF
if (pet)
mask1 = 0x7FFFFFFF; // for hunters and other classes with pets
Powers powerType = player->getPowerType();
data << (uint32) mask1; // group update mask
data << (uint16) MEMBER_STATUS_ONLINE; // member's online status
data << (uint32) player->GetHealth(); // GROUP_UPDATE_FLAG_CUR_HP
data << (uint32) player->GetMaxHealth(); // GROUP_UPDATE_FLAG_MAX_HP
data << (uint8) powerType; // GROUP_UPDATE_FLAG_POWER_TYPE
data << (uint16) player->GetPower(powerType); // GROUP_UPDATE_FLAG_CUR_POWER
data << (uint16) player->GetMaxPower(powerType); // GROUP_UPDATE_FLAG_MAX_POWER
data << (uint16) player->getLevel(); // GROUP_UPDATE_FLAG_LEVEL
data << (uint16) player->GetZoneId(); // GROUP_UPDATE_FLAG_ZONE
data << (uint16) player->GetPositionX(); // GROUP_UPDATE_FLAG_POSITION
data << (uint16) player->GetPositionY(); // GROUP_UPDATE_FLAG_POSITION
uint64 auramask = 0;
size_t maskPos = data.wpos();
data << (uint64) auramask; // placeholder
for (uint8 i = 0; i < MAX_AURAS; ++i)
{
if (AuraApplication * aurApp = player->GetVisibleAura(i))
{
auramask |= (uint64(1) << i);
data << (uint32) aurApp->GetBase()->GetId();
data << (uint8) 1;
}
}
data.put<uint64>(maskPos, auramask); // GROUP_UPDATE_FLAG_AURAS
if (pet)
{
Powers petpowertype = pet->getPowerType();
data << (uint64) pet->GetGUID(); // GROUP_UPDATE_FLAG_PET_GUID
data << pet->GetName(); // GROUP_UPDATE_FLAG_PET_NAME
data << (uint16) pet->GetDisplayId(); // GROUP_UPDATE_FLAG_PET_MODEL_ID
data << (uint32) pet->GetHealth(); // GROUP_UPDATE_FLAG_PET_CUR_HP
data << (uint32) pet->GetMaxHealth(); // GROUP_UPDATE_FLAG_PET_MAX_HP
data << (uint8) petpowertype; // GROUP_UPDATE_FLAG_PET_POWER_TYPE
data << (uint16) pet->GetPower(petpowertype); // GROUP_UPDATE_FLAG_PET_CUR_POWER
data << (uint16) pet->GetMaxPower(petpowertype); // GROUP_UPDATE_FLAG_PET_MAX_POWER
uint64 petauramask = 0;
size_t petMaskPos = data.wpos();
data << (uint64) petauramask; // placeholder
for (uint8 i = 0; i < MAX_AURAS; ++i)
{
if (AuraApplication * auraApp = pet->GetVisibleAura(i))
{
petauramask |= (uint64(1) << i);
data << (uint32) auraApp->GetBase()->GetId();
data << (uint8) 1;
}
}
data.put<uint64>(petMaskPos, petauramask); // GROUP_UPDATE_FLAG_PET_AURAS
}
else
{
data << (uint8) 0; // GROUP_UPDATE_FLAG_PET_NAME
data << (uint64) 0; // GROUP_UPDATE_FLAG_PET_AURAS
}
SendPacket(&data);
}
/*!*/void WorldSession::HandleRequestRaidInfoOpcode(WorldPacket & /*recv_data*/)
{
// every time the player checks the character screen
_player->SendRaidInfo();
}
/*void WorldSession::HandleGroupCancelOpcode(WorldPacket & recv_data)
{
sLog->outDebug("WORLD: got CMSG_GROUP_CANCEL.");
}*/
void WorldSession::HandleOptOutOfLootOpcode(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_OPT_OUT_OF_LOOT");
uint32 passOnLoot;
recv_data >> passOnLoot; // 1 always pass, 0 do not pass
// ignore if player not loaded
if (!GetPlayer()) // needed because STATUS_AUTHED
{
if (passOnLoot != 0)
sLog->outError("CMSG_OPT_OUT_OF_LOOT value<>0 for not-loaded character!");
return;
}
GetPlayer()->SetPassOnGroupLoot(passOnLoot);
}
+570
View File
@@ -0,0 +1,570 @@
/*
* Copyright (C) 2008-2012 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 "WorldPacket.h"
#include "WorldSession.h"
#include "World.h"
#include "ObjectMgr.h"
#include "GuildMgr.h"
#include "Log.h"
#include "Opcodes.h"
#include "Guild.h"
#include "GossipDef.h"
#include "SocialMgr.h"
// Helper for getting guild object of session's player.
// If guild does not exist, sends error (if necessary).
inline Guild* _GetPlayerGuild(WorldSession* session, bool sendError = false)
{
if (uint32 guildId = session->GetPlayer()->GetGuildId()) // If guild id = 0, player is not in guild
if (Guild* guild = sGuildMgr->GetGuildById(guildId)) // Find guild by id
return guild;
if (sendError)
Guild::SendCommandResult(session, GUILD_CREATE_S, ERR_GUILD_PLAYER_NOT_IN_GUILD);
return NULL;
}
void WorldSession::HandleGuildQueryOpcode(WorldPacket& recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_QUERY");
uint32 guildId;
recvPacket >> guildId;
// Use received guild id to access guild method (not player's guild id)
if (Guild* guild = sGuildMgr->GetGuildById(guildId))
guild->HandleQuery(this);
else
Guild::SendCommandResult(this, GUILD_CREATE_S, ERR_GUILD_PLAYER_NOT_IN_GUILD);
}
void WorldSession::HandleGuildCreateOpcode(WorldPacket& recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_CREATE");
std::string name;
recvPacket >> name;
if (!GetPlayer()->GetGuildId()) // Player cannot be in guild
{
Guild* guild = new Guild();
if (guild->Create(GetPlayer(), name))
sGuildMgr->AddGuild(guild);
else
delete guild;
}
}
void WorldSession::HandleGuildInviteOpcode(WorldPacket& recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_INVITE");
std::string invitedName;
recvPacket >> invitedName;
if (normalizePlayerName(invitedName))
if (Guild* guild = _GetPlayerGuild(this, true))
guild->HandleInviteMember(this, invitedName);
}
void WorldSession::HandleGuildRemoveOpcode(WorldPacket& recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_REMOVE");
std::string playerName;
recvPacket >> playerName;
if (normalizePlayerName(playerName))
if (Guild* guild = _GetPlayerGuild(this, true))
guild->HandleRemoveMember(this, playerName);
}
void WorldSession::HandleGuildAcceptOpcode(WorldPacket& /*recvPacket*/)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_ACCEPT");
// Player cannot be in guild
if (!GetPlayer()->GetGuildId())
// Guild where player was invited must exist
if (Guild* guild = sGuildMgr->GetGuildById(GetPlayer()->GetGuildIdInvited()))
guild->HandleAcceptMember(this);
}
void WorldSession::HandleGuildDeclineOpcode(WorldPacket& /*recvPacket*/)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_DECLINE");
GetPlayer()->SetGuildIdInvited(0);
GetPlayer()->SetInGuild(0);
}
void WorldSession::HandleGuildInfoOpcode(WorldPacket& /*recvPacket*/)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_INFO");
if (Guild* guild = _GetPlayerGuild(this, true))
guild->SendInfo(this);
}
void WorldSession::HandleGuildRosterOpcode(WorldPacket& /*recvPacket*/)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_ROSTER");
if (Guild* guild = _GetPlayerGuild(this))
guild->HandleRoster(this);
}
void WorldSession::HandleGuildPromoteOpcode(WorldPacket& recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_PROMOTE");
std::string playerName;
recvPacket >> playerName;
if (normalizePlayerName(playerName))
if (Guild* guild = _GetPlayerGuild(this, true))
guild->HandleUpdateMemberRank(this, playerName, false);
}
void WorldSession::HandleGuildDemoteOpcode(WorldPacket& recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_DEMOTE");
std::string playerName;
recvPacket >> playerName;
if (normalizePlayerName(playerName))
if (Guild* guild = _GetPlayerGuild(this, true))
guild->HandleUpdateMemberRank(this, playerName, true);
}
void WorldSession::HandleGuildLeaveOpcode(WorldPacket& /*recvPacket*/)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_LEAVE");
if (Guild* guild = _GetPlayerGuild(this, true))
guild->HandleLeaveMember(this);
}
void WorldSession::HandleGuildDisbandOpcode(WorldPacket& /*recvPacket*/)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_DISBAND");
if (Guild* guild = _GetPlayerGuild(this, true))
guild->HandleDisband(this);
}
void WorldSession::HandleGuildLeaderOpcode(WorldPacket& recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_LEADER");
std::string name;
recvPacket >> name;
if (normalizePlayerName(name))
if (Guild* guild = _GetPlayerGuild(this, true))
guild->HandleSetLeader(this, name);
}
void WorldSession::HandleGuildMOTDOpcode(WorldPacket& recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_MOTD");
std::string motd; // Empty by default
if (!recvPacket.empty())
recvPacket >> motd;
if (Guild* guild = _GetPlayerGuild(this, true))
guild->HandleSetMOTD(this, motd);
}
void WorldSession::HandleGuildSetPublicNoteOpcode(WorldPacket& recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_SET_PUBLIC_NOTE");
std::string playerName;
recvPacket >> playerName;
std::string publicNote;
recvPacket >> publicNote;
if (normalizePlayerName(playerName))
if (Guild* guild = _GetPlayerGuild(this, true))
guild->HandleSetMemberNote(this, playerName, publicNote, false);
}
void WorldSession::HandleGuildSetOfficerNoteOpcode(WorldPacket& recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_SET_OFFICER_NOTE");
std::string playerName;
recvPacket >> playerName;
std::string officerNote;
recvPacket >> officerNote;
if (normalizePlayerName(playerName))
if (Guild* guild = _GetPlayerGuild(this, true))
guild->HandleSetMemberNote(this, playerName, officerNote, true);
}
void WorldSession::HandleGuildRankOpcode(WorldPacket& recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_RANK");
Guild* guild = _GetPlayerGuild(this, true);
if (!guild)
{
recvPacket.rpos(recvPacket.wpos());
return;
}
uint32 rankId;
recvPacket >> rankId;
uint32 rights;
recvPacket >> rights;
std::string rankName;
recvPacket >> rankName;
uint32 money;
recvPacket >> money;
GuildBankRightsAndSlotsVec rightsAndSlots(GUILD_BANK_MAX_TABS);
for (uint8 tabId = 0; tabId < GUILD_BANK_MAX_TABS; ++tabId)
{
uint32 bankRights;
uint32 slots;
recvPacket >> bankRights;
recvPacket >> slots;
rightsAndSlots[tabId] = GuildBankRightsAndSlots(uint8(bankRights), slots);
}
guild->HandleSetRankInfo(this, rankId, rankName, rights, money, rightsAndSlots);
}
void WorldSession::HandleGuildAddRankOpcode(WorldPacket& recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_ADD_RANK");
std::string rankName;
recvPacket >> rankName;
if (Guild* guild = _GetPlayerGuild(this, true))
guild->HandleAddNewRank(this, rankName);
}
void WorldSession::HandleGuildDelRankOpcode(WorldPacket& /*recvPacket*/)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_DEL_RANK");
if (Guild* guild = _GetPlayerGuild(this, true))
guild->HandleRemoveLowestRank(this);
}
void WorldSession::HandleGuildChangeInfoTextOpcode(WorldPacket& recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_INFO_TEXT");
std::string info;
recvPacket >> info;
if (Guild* guild = _GetPlayerGuild(this, true))
guild->HandleSetInfo(this, info);
}
void WorldSession::HandleSaveGuildEmblemOpcode(WorldPacket& recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received MSG_SAVE_GUILD_EMBLEM");
uint64 vendorGuid;
recvPacket >> vendorGuid;
EmblemInfo emblemInfo;
emblemInfo.ReadPacket(recvPacket);
if (GetPlayer()->GetNPCIfCanInteractWith(vendorGuid, UNIT_NPC_FLAG_TABARDDESIGNER))
{
// Remove fake death
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
if (Guild* guild = _GetPlayerGuild(this))
guild->HandleSetEmblem(this, emblemInfo);
else
// "You are not part of a guild!";
Guild::SendSaveEmblemResult(this, ERR_GUILDEMBLEM_NOGUILD);
}
else
{
// "That's not an emblem vendor!"
Guild::SendSaveEmblemResult(this, ERR_GUILDEMBLEM_INVALIDVENDOR);
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleSaveGuildEmblemOpcode - Unit (GUID: %u) not found or you can't interact with him.", GUID_LOPART(vendorGuid));
}
}
void WorldSession::HandleGuildEventLogQueryOpcode(WorldPacket& /* recvPacket */)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received (MSG_GUILD_EVENT_LOG_QUERY)");
if (Guild* guild = _GetPlayerGuild(this))
guild->SendEventLog(this);
}
void WorldSession::HandleGuildBankMoneyWithdrawn(WorldPacket & /* recv_data */)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received (MSG_GUILD_BANK_MONEY_WITHDRAWN)");
if (Guild* guild = _GetPlayerGuild(this))
guild->SendMoneyInfo(this);
}
void WorldSession::HandleGuildPermissions(WorldPacket& /* recv_data */)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received (MSG_GUILD_PERMISSIONS)");
if (Guild* guild = _GetPlayerGuild(this))
guild->SendPermissions(this);
}
// Called when clicking on Guild bank gameobject
void WorldSession::HandleGuildBankerActivate(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received (CMSG_GUILD_BANKER_ACTIVATE)");
uint64 GoGuid;
recv_data >> GoGuid;
uint8 unk;
recv_data >> unk;
if (GetPlayer()->GetGameObjectIfCanInteractWith(GoGuid, GAMEOBJECT_TYPE_GUILD_BANK))
{
if (Guild* guild = _GetPlayerGuild(this))
guild->SendBankTabsInfo(this);
else
Guild::SendCommandResult(this, GUILD_UNK1, ERR_GUILD_PLAYER_NOT_IN_GUILD);
}
}
// Called when opening guild bank tab only (first one)
void WorldSession::HandleGuildBankQueryTab(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received (CMSG_GUILD_BANK_QUERY_TAB)");
uint64 GoGuid;
recv_data >> GoGuid;
uint8 tabId;
recv_data >> tabId;
uint8 unk1;
recv_data >> unk1;
if (GetPlayer()->GetGameObjectIfCanInteractWith(GoGuid, GAMEOBJECT_TYPE_GUILD_BANK))
if (Guild* guild = _GetPlayerGuild(this))
guild->SendBankTabData(this, tabId);
}
void WorldSession::HandleGuildBankDepositMoney(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received (CMSG_GUILD_BANK_DEPOSIT_MONEY)");
uint64 GoGuid;
recv_data >> GoGuid;
uint32 money;
recv_data >> money;
if (GetPlayer()->GetGameObjectIfCanInteractWith(GoGuid, GAMEOBJECT_TYPE_GUILD_BANK))
if (money && GetPlayer()->HasEnoughMoney(money))
if (Guild* guild = _GetPlayerGuild(this))
guild->HandleMemberDepositMoney(this, money);
}
void WorldSession::HandleGuildBankWithdrawMoney(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received (CMSG_GUILD_BANK_WITHDRAW_MONEY)");
uint64 GoGuid;
recv_data >> GoGuid;
uint32 money;
recv_data >> money;
if (money)
if (GetPlayer()->GetGameObjectIfCanInteractWith(GoGuid, GAMEOBJECT_TYPE_GUILD_BANK))
if (Guild* guild = _GetPlayerGuild(this))
guild->HandleMemberWithdrawMoney(this, money);
}
void WorldSession::HandleGuildBankSwapItems(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received (CMSG_GUILD_BANK_SWAP_ITEMS)");
uint64 GoGuid;
recv_data >> GoGuid;
if (!GetPlayer()->GetGameObjectIfCanInteractWith(GoGuid, GAMEOBJECT_TYPE_GUILD_BANK))
{
recv_data.rfinish(); // Prevent additional spam at rejected packet
return;
}
Guild* guild = _GetPlayerGuild(this);
if (!guild)
{
recv_data.rfinish(); // Prevent additional spam at rejected packet
return;
}
uint8 bankToBank;
recv_data >> bankToBank;
uint8 tabId;
uint8 slotId;
uint32 itemEntry;
uint32 splitedAmount = 0;
if (bankToBank)
{
uint8 destTabId;
recv_data >> destTabId;
uint8 destSlotId;
recv_data >> destSlotId;
recv_data.read_skip<uint32>(); // Always 0
recv_data >> tabId;
recv_data >> slotId;
recv_data >> itemEntry;
recv_data.read_skip<uint8>(); // Always 0
recv_data >> splitedAmount;
guild->SwapItems(GetPlayer(), tabId, slotId, destTabId, destSlotId, splitedAmount);
}
else
{
uint8 playerBag = NULL_BAG;
uint8 playerSlotId = NULL_SLOT;
uint8 toChar = 1;
recv_data >> tabId;
recv_data >> slotId;
recv_data >> itemEntry;
uint8 autoStore;
recv_data >> autoStore;
if (autoStore)
{
recv_data.read_skip<uint32>(); // autoStoreCount
recv_data.read_skip<uint8>(); // ToChar (?), always and expected to be 1 (autostore only triggered in Bank -> Char)
recv_data.read_skip<uint32>(); // Always 0
}
else
{
recv_data >> playerBag;
recv_data >> playerSlotId;
recv_data >> toChar;
recv_data >> splitedAmount;
}
// Player <-> Bank
// Allow to work with inventory only
if (!Player::IsInventoryPos(playerBag, playerSlotId) && !(playerBag == NULL_BAG && playerSlotId == NULL_SLOT))
GetPlayer()->SendEquipError(EQUIP_ERR_NONE, NULL);
else
guild->SwapItemsWithInventory(GetPlayer(), toChar, tabId, slotId, playerBag, playerSlotId, splitedAmount);
}
}
void WorldSession::HandleGuildBankBuyTab(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received (CMSG_GUILD_BANK_BUY_TAB)");
uint64 GoGuid;
recv_data >> GoGuid;
uint8 tabId;
recv_data >> tabId;
if (GetPlayer()->GetGameObjectIfCanInteractWith(GoGuid, GAMEOBJECT_TYPE_GUILD_BANK))
if (Guild* guild = _GetPlayerGuild(this))
guild->HandleBuyBankTab(this, tabId);
}
void WorldSession::HandleGuildBankUpdateTab(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received (CMSG_GUILD_BANK_UPDATE_TAB)");
uint64 GoGuid;
recv_data >> GoGuid;
uint8 tabId;
recv_data >> tabId;
std::string name;
recv_data >> name;
std::string icon;
recv_data >> icon;
if (!name.empty() && !icon.empty())
if (GetPlayer()->GetGameObjectIfCanInteractWith(GoGuid, GAMEOBJECT_TYPE_GUILD_BANK))
if (Guild* guild = _GetPlayerGuild(this))
guild->HandleSetBankTabInfo(this, tabId, name, icon);
}
void WorldSession::HandleGuildBankLogQuery(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received (MSG_GUILD_BANK_LOG_QUERY)");
uint8 tabId;
recv_data >> tabId;
if (Guild* guild = _GetPlayerGuild(this))
guild->SendBankLog(this, tabId);
}
void WorldSession::HandleQueryGuildBankTabText(WorldPacket &recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received MSG_QUERY_GUILD_BANK_TEXT");
uint8 tabId;
recv_data >> tabId;
if (Guild* guild = _GetPlayerGuild(this))
guild->SendBankTabText(this, tabId);
}
void WorldSession::HandleSetGuildBankTabText(WorldPacket &recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_SET_GUILD_BANK_TEXT");
uint8 tabId;
recv_data >> tabId;
std::string text;
recv_data >> text;
if (Guild* guild = _GetPlayerGuild(this))
guild->SetBankTabText(tabId, text);
}
File diff suppressed because it is too large Load Diff
+664
View File
@@ -0,0 +1,664 @@
/*
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
*
* 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 "WorldSession.h"
#include "WorldPacket.h"
#include "DBCStores.h"
#include "Player.h"
#include "Group.h"
#include "LFGMgr.h"
#include "ObjectMgr.h"
#include "GroupMgr.h"
#include "InstanceScript.h"
void BuildPlayerLockDungeonBlock(WorldPacket& data, const LfgLockMap& lock)
{
data << uint32(lock.size()); // Size of lock dungeons
for (LfgLockMap::const_iterator it = lock.begin(); it != lock.end(); ++it)
{
data << uint32(it->first); // Dungeon entry (id + type)
data << uint32(it->second); // Lock status
}
}
void BuildPartyLockDungeonBlock(WorldPacket& data, const LfgLockPartyMap& lockMap)
{
data << uint8(lockMap.size());
for (LfgLockPartyMap::const_iterator it = lockMap.begin(); it != lockMap.end(); ++it)
{
data << uint64(it->first); // Player guid
BuildPlayerLockDungeonBlock(data, it->second);
}
}
void WorldSession::HandleLfgJoinOpcode(WorldPacket& recv_data)
{
if (!sWorld->getBoolConfig(CONFIG_DUNGEON_FINDER_ENABLE) ||
(GetPlayer()->GetGroup() && GetPlayer()->GetGroup()->GetLeaderGUID() != GetPlayer()->GetGUID() &&
(GetPlayer()->GetGroup()->GetMembersCount() == MAXGROUPSIZE || !GetPlayer()->GetGroup()->isLFGGroup())))
{
recv_data.rfinish();
return;
}
uint8 numDungeons;
uint32 dungeon;
uint32 roles;
recv_data >> roles;
recv_data.read_skip<uint16>(); // uint8 (always 0) - uint8 (always 0)
recv_data >> numDungeons;
if (!numDungeons)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_JOIN [" UI64FMTD "] no dungeons selected", GetPlayer()->GetGUID());
recv_data.rfinish();
return;
}
LfgDungeonSet newDungeons;
for (int8 i = 0 ; i < numDungeons; ++i)
{
recv_data >> dungeon;
newDungeons.insert((dungeon & 0x00FFFFFF)); // remove the type from the dungeon entry
}
recv_data.read_skip<uint32>(); // for 0..uint8 (always 3) { uint8 (always 0) }
std::string comment;
recv_data >> comment;
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_JOIN [" UI64FMTD "] roles: %u, Dungeons: %u, Comment: %s", GetPlayer()->GetGUID(), roles, uint8(newDungeons.size()), comment.c_str());
sLFGMgr->Join(GetPlayer(), uint8(roles), newDungeons, comment);
}
void WorldSession::HandleLfgLeaveOpcode(WorldPacket& /*recv_data*/)
{
Group* grp = GetPlayer()->GetGroup();
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_LEAVE [" UI64FMTD "] in group: %u", GetPlayer()->GetGUID(), grp ? 1 : 0);
// Check cheating - only leader can leave the queue
if (!grp || grp->GetLeaderGUID() == GetPlayer()->GetGUID())
sLFGMgr->Leave(GetPlayer(), grp);
}
void WorldSession::HandleLfgProposalResultOpcode(WorldPacket& recv_data)
{
uint32 lfgGroupID; // Internal lfgGroupID
bool accept; // Accept to join?
recv_data >> lfgGroupID;
recv_data >> accept;
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_PROPOSAL_RESULT [" UI64FMTD "] proposal: %u accept: %u", GetPlayer()->GetGUID(), lfgGroupID, accept ? 1 : 0);
sLFGMgr->UpdateProposal(lfgGroupID, GetPlayer()->GetGUID(), accept);
}
void WorldSession::HandleLfgSetRolesOpcode(WorldPacket& recv_data)
{
uint8 roles;
recv_data >> roles; // Player Group Roles
uint64 guid = GetPlayer()->GetGUID();
Group* grp = GetPlayer()->GetGroup();
if (!grp)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_SET_ROLES [" UI64FMTD "] Not in group", guid);
return;
}
uint64 gguid = grp->GetGUID();
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_SET_ROLES: Group [" UI64FMTD "], Player [" UI64FMTD "], Roles: %u", gguid, guid, roles);
sLFGMgr->UpdateRoleCheck(gguid, guid, roles);
}
void WorldSession::HandleLfgSetCommentOpcode(WorldPacket& recv_data)
{
std::string comment;
recv_data >> comment;
uint64 guid = GetPlayer()->GetGUID();
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_SET_LFG_COMMENT [" UI64FMTD "] comment: %s", guid, comment.c_str());
sLFGMgr->SetComment(guid, comment);
}
void WorldSession::HandleLfgSetBootVoteOpcode(WorldPacket& recv_data)
{
bool agree; // Agree to kick player
recv_data >> agree;
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_SET_BOOT_VOTE [" UI64FMTD "] agree: %u", GetPlayer()->GetGUID(), agree ? 1 : 0);
sLFGMgr->UpdateBoot(GetPlayer(), agree);
}
void WorldSession::HandleLfgTeleportOpcode(WorldPacket& recv_data)
{
bool out;
recv_data >> out;
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_TELEPORT [" UI64FMTD "] out: %u", GetPlayer()->GetGUID(), out ? 1 : 0);
sLFGMgr->TeleportPlayer(GetPlayer(), out, true);
}
void WorldSession::HandleLfgPlayerLockInfoRequestOpcode(WorldPacket& /*recv_data*/)
{
uint64 guid = GetPlayer()->GetGUID();
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFD_PLAYER_LOCK_INFO_REQUEST [" UI64FMTD "]", guid);
// Get Random dungeons that can be done at a certain level and expansion
// FIXME - Should return seasonals (when not disabled)
LfgDungeonSet randomDungeons;
uint8 level = GetPlayer()->getLevel();
uint8 expansion = GetPlayer()->GetSession()->Expansion();
for (uint32 i = 0; i < sLFGDungeonStore.GetNumRows(); ++i)
{
LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(i);
if (dungeon && dungeon->type == LFG_TYPE_RANDOM && dungeon->expansion <= expansion &&
dungeon->minlevel <= level && level <= dungeon->maxlevel)
randomDungeons.insert(dungeon->Entry());
}
// Get player locked Dungeons
LfgLockMap lock = sLFGMgr->GetLockedDungeons(guid);
uint32 rsize = uint32(randomDungeons.size());
uint32 lsize = uint32(lock.size());
sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_PLAYER_INFO [" UI64FMTD "]", guid);
WorldPacket data(SMSG_LFG_PLAYER_INFO, 1 + rsize * (4 + 1 + 4 + 4 + 4 + 4 + 1 + 4 + 4 + 4) + 4 + lsize * (1 + 4 + 4 + 4 + 4 + 1 + 4 + 4 + 4));
data << uint8(randomDungeons.size()); // Random Dungeon count
for (LfgDungeonSet::const_iterator it = randomDungeons.begin(); it != randomDungeons.end(); ++it)
{
data << uint32(*it); // Dungeon Entry (id + type)
LfgReward const* reward = sLFGMgr->GetRandomDungeonReward(*it, level);
Quest const* qRew = NULL;
uint8 done = 0;
if (reward)
{
qRew = sObjectMgr->GetQuestTemplate(reward->reward[0].questId);
if (qRew)
{
done = !GetPlayer()->CanRewardQuest(qRew, false);
if (done)
qRew = sObjectMgr->GetQuestTemplate(reward->reward[1].questId);
}
}
if (qRew)
{
data << uint8(done);
data << uint32(qRew->GetRewOrReqMoney());
data << uint32(qRew->XPValue(GetPlayer()));
data << uint32(reward->reward[done].variableMoney);
data << uint32(reward->reward[done].variableXP);
data << uint8(qRew->GetRewItemsCount());
if (qRew->GetRewItemsCount())
{
ItemTemplate const* iProto = NULL;
for (uint8 i = 0; i < QUEST_REWARDS_COUNT; ++i)
{
if (!qRew->RewardItemId[i])
continue;
iProto = sObjectMgr->GetItemTemplate(qRew->RewardItemId[i]);
data << uint32(qRew->RewardItemId[i]);
data << uint32(iProto ? iProto->DisplayInfoID : 0);
data << uint32(qRew->RewardItemIdCount[i]);
}
}
}
else
{
data << uint8(0);
data << uint32(0);
data << uint32(0);
data << uint32(0);
data << uint32(0);
data << uint8(0);
}
}
BuildPlayerLockDungeonBlock(data, lock);
SendPacket(&data);
}
void WorldSession::HandleLfgPartyLockInfoRequestOpcode(WorldPacket& /*recv_data*/)
{
uint64 guid = GetPlayer()->GetGUID();
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFD_PARTY_LOCK_INFO_REQUEST [" UI64FMTD "]", guid);
Group* grp = GetPlayer()->GetGroup();
if (!grp)
return;
// Get the locked dungeons of the other party members
LfgLockPartyMap lockMap;
for (GroupReference* itr = grp->GetFirstMember(); itr != NULL; itr = itr->next())
{
Player* plrg = itr->getSource();
if (!plrg)
continue;
uint64 pguid = plrg->GetGUID();
if (pguid == guid)
continue;
lockMap[pguid] = sLFGMgr->GetLockedDungeons(pguid);
}
uint32 size = 0;
for (LfgLockPartyMap::const_iterator it = lockMap.begin(); it != lockMap.end(); ++it)
size += 8 + 4 + uint32(it->second.size()) * (4 + 4);
sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_PARTY_INFO [" UI64FMTD "]", guid);
WorldPacket data(SMSG_LFG_PARTY_INFO, 1 + size);
BuildPartyLockDungeonBlock(data, lockMap);
SendPacket(&data);
}
void WorldSession::HandleLfrSearchOpcode(WorldPacket& recv_data)
{
uint32 entry; // Raid id to search
recv_data >> entry;
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_SEARCH_LFG_JOIN [" UI64FMTD "] dungeon entry: %u", GetPlayer()->GetGUID(), entry);
//SendLfrUpdateListOpcode(entry);
}
void WorldSession::HandleLfrLeaveOpcode(WorldPacket& recv_data)
{
uint32 dungeonId; // Raid id queue to leave
recv_data >> dungeonId;
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_SEARCH_LFG_LEAVE [" UI64FMTD "] dungeonId: %u", GetPlayer()->GetGUID(), dungeonId);
//sLFGMgr->LeaveLfr(GetPlayer(), dungeonId);
}
void WorldSession::SendLfgUpdatePlayer(const LfgUpdateData& updateData)
{
bool queued = false;
bool extrainfo = false;
switch (updateData.updateType)
{
case LFG_UPDATETYPE_JOIN_PROPOSAL:
case LFG_UPDATETYPE_ADDED_TO_QUEUE:
queued = true;
extrainfo = true;
break;
//case LFG_UPDATETYPE_CLEAR_LOCK_LIST: // TODO: Sometimes has extrainfo - Check ocurrences...
case LFG_UPDATETYPE_PROPOSAL_BEGIN:
extrainfo = true;
break;
default:
break;
}
uint64 guid = GetPlayer()->GetGUID();
uint8 size = uint8(updateData.dungeons.size());
sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_UPDATE_PLAYER [" UI64FMTD "] updatetype: %u", guid, updateData.updateType);
WorldPacket data(SMSG_LFG_UPDATE_PLAYER, 1 + 1 + (extrainfo ? 1 : 0) * (1 + 1 + 1 + 1 + size * 4 + updateData.comment.length()));
data << uint8(updateData.updateType); // Lfg Update type
data << uint8(extrainfo); // Extra info
if (extrainfo)
{
data << uint8(queued); // Join the queue
data << uint8(0); // unk - Always 0
data << uint8(0); // unk - Always 0
data << uint8(size);
if (size)
for (LfgDungeonSet::const_iterator it = updateData.dungeons.begin(); it != updateData.dungeons.end(); ++it)
data << uint32(*it);
data << updateData.comment;
}
SendPacket(&data);
}
void WorldSession::SendLfgUpdateParty(const LfgUpdateData& updateData)
{
bool join = false;
bool extrainfo = false;
bool queued = false;
switch (updateData.updateType)
{
case LFG_UPDATETYPE_JOIN_PROPOSAL:
extrainfo = true;
break;
case LFG_UPDATETYPE_ADDED_TO_QUEUE:
extrainfo = true;
join = true;
queued = true;
break;
case LFG_UPDATETYPE_CLEAR_LOCK_LIST:
// join = true; // TODO: Sometimes queued and extrainfo - Check ocurrences...
queued = true;
break;
case LFG_UPDATETYPE_PROPOSAL_BEGIN:
extrainfo = true;
join = true;
break;
default:
break;
}
uint64 guid = GetPlayer()->GetGUID();
uint8 size = uint8(updateData.dungeons.size());
sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_UPDATE_PARTY [" UI64FMTD "] updatetype: %u", guid, updateData.updateType);
WorldPacket data(SMSG_LFG_UPDATE_PARTY, 1 + 1 + (extrainfo ? 1 : 0) * (1 + 1 + 1 + 1 + 1 + size * 4 + updateData.comment.length()));
data << uint8(updateData.updateType); // Lfg Update type
data << uint8(extrainfo); // Extra info
if (extrainfo)
{
data << uint8(join); // LFG Join
data << uint8(queued); // Join the queue
data << uint8(0); // unk - Always 0
data << uint8(0); // unk - Always 0
for (uint8 i = 0; i < 3; ++i)
data << uint8(0); // unk - Always 0
data << uint8(size);
if (size)
for (LfgDungeonSet::const_iterator it = updateData.dungeons.begin(); it != updateData.dungeons.end(); ++it)
data << uint32(*it);
data << updateData.comment;
}
SendPacket(&data);
}
void WorldSession::SendLfgRoleChosen(uint64 guid, uint8 roles)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_ROLE_CHOSEN [" UI64FMTD "] guid: [" UI64FMTD "] roles: %u", GetPlayer()->GetGUID(), guid, roles);
WorldPacket data(SMSG_LFG_ROLE_CHOSEN, 8 + 1 + 4);
data << uint64(guid); // Guid
data << uint8(roles > 0); // Ready
data << uint32(roles); // Roles
SendPacket(&data);
}
void WorldSession::SendLfgRoleCheckUpdate(const LfgRoleCheck* pRoleCheck)
{
ASSERT(pRoleCheck);
LfgDungeonSet dungeons;
if (pRoleCheck->rDungeonId)
dungeons.insert(pRoleCheck->rDungeonId);
else
dungeons = pRoleCheck->dungeons;
sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_ROLE_CHECK_UPDATE [" UI64FMTD "]", GetPlayer()->GetGUID());
WorldPacket data(SMSG_LFG_ROLE_CHECK_UPDATE, 4 + 1 + 1 + dungeons.size() * 4 + 1 + pRoleCheck->roles.size() * (8 + 1 + 4 + 1));
data << uint32(pRoleCheck->state); // Check result
data << uint8(pRoleCheck->state == LFG_ROLECHECK_INITIALITING);
data << uint8(dungeons.size()); // Number of dungeons
if (!dungeons.empty())
{
for (LfgDungeonSet::iterator it = dungeons.begin(); it != dungeons.end(); ++it)
{
LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(*it);
data << uint32(dungeon ? dungeon->Entry() : 0); // Dungeon
}
}
data << uint8(pRoleCheck->roles.size()); // Players in group
if (!pRoleCheck->roles.empty())
{
// Leader info MUST be sent 1st :S
uint64 guid = pRoleCheck->leader;
uint8 roles = pRoleCheck->roles.find(guid)->second;
data << uint64(guid); // Guid
data << uint8(roles > 0); // Ready
data << uint32(roles); // Roles
Player* player = ObjectAccessor::FindPlayer(guid);
data << uint8(player ? player->getLevel() : 0); // Level
for (LfgRolesMap::const_iterator it = pRoleCheck->roles.begin(); it != pRoleCheck->roles.end(); ++it)
{
if (it->first == pRoleCheck->leader)
continue;
guid = it->first;
roles = it->second;
data << uint64(guid); // Guid
data << uint8(roles > 0); // Ready
data << uint32(roles); // Roles
player = ObjectAccessor::FindPlayer(guid);
data << uint8(player ? player->getLevel() : 0); // Level
}
}
SendPacket(&data);
}
void WorldSession::SendLfgJoinResult(const LfgJoinResultData& joinData)
{
uint32 size = 0;
for (LfgLockPartyMap::const_iterator it = joinData.lockmap.begin(); it != joinData.lockmap.end(); ++it)
size += 8 + 4 + uint32(it->second.size()) * (4 + 4);
sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_JOIN_RESULT [" UI64FMTD "] checkResult: %u checkValue: %u", GetPlayer()->GetGUID(), joinData.result, joinData.state);
WorldPacket data(SMSG_LFG_JOIN_RESULT, 4 + 4 + size);
data << uint32(joinData.result); // Check Result
data << uint32(joinData.state); // Check Value
if (!joinData.lockmap.empty())
BuildPartyLockDungeonBlock(data, joinData.lockmap);
SendPacket(&data);
}
void WorldSession::SendLfgQueueStatus(uint32 dungeon, int32 waitTime, int32 avgWaitTime, int32 waitTimeTanks, int32 waitTimeHealer, int32 waitTimeDps, uint32 queuedTime, uint8 tanks, uint8 healers, uint8 dps)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_QUEUE_STATUS [" UI64FMTD "] dungeon: %u - waitTime: %d - avgWaitTime: %d - waitTimeTanks: %d - waitTimeHealer: %d - waitTimeDps: %d - queuedTime: %u - tanks: %u - healers: %u - dps: %u", GetPlayer()->GetGUID(), dungeon, waitTime, avgWaitTime, waitTimeTanks, waitTimeHealer, waitTimeDps, queuedTime, tanks, healers, dps);
WorldPacket data(SMSG_LFG_QUEUE_STATUS, 4 + 4 + 4 + 4 + 4 +4 + 1 + 1 + 1 + 4);
data << uint32(dungeon); // Dungeon
data << int32(avgWaitTime); // Average Wait time
data << int32(waitTime); // Wait Time
data << int32(waitTimeTanks); // Wait Tanks
data << int32(waitTimeHealer); // Wait Healers
data << int32(waitTimeDps); // Wait Dps
data << uint8(tanks); // Tanks needed
data << uint8(healers); // Healers needed
data << uint8(dps); // Dps needed
data << uint32(queuedTime); // Player wait time in queue
SendPacket(&data);
}
void WorldSession::SendLfgPlayerReward(uint32 rdungeonEntry, uint32 sdungeonEntry, uint8 done, const LfgReward* reward, const Quest* qRew)
{
if (!rdungeonEntry || !sdungeonEntry || !qRew)
return;
uint8 itemNum = uint8(qRew ? qRew->GetRewItemsCount() : 0);
sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_PLAYER_REWARD [" UI64FMTD "] rdungeonEntry: %u - sdungeonEntry: %u - done: %u", GetPlayer()->GetGUID(), rdungeonEntry, sdungeonEntry, done);
WorldPacket data(SMSG_LFG_PLAYER_REWARD, 4 + 4 + 1 + 4 + 4 + 4 + 4 + 4 + 1 + itemNum * (4 + 4 + 4));
data << uint32(rdungeonEntry); // Random Dungeon Finished
data << uint32(sdungeonEntry); // Dungeon Finished
data << uint8(done);
data << uint32(1);
data << uint32(qRew->GetRewOrReqMoney());
data << uint32(qRew->XPValue(GetPlayer()));
data << uint32(reward->reward[done].variableMoney);
data << uint32(reward->reward[done].variableXP);
data << uint8(itemNum);
if (itemNum)
{
ItemTemplate const* iProto = NULL;
for (uint8 i = 0; i < QUEST_REWARDS_COUNT; ++i)
{
if (!qRew->RewardItemId[i])
continue;
iProto = sObjectMgr->GetItemTemplate(qRew->RewardItemId[i]);
data << uint32(qRew->RewardItemId[i]);
data << uint32(iProto ? iProto->DisplayInfoID : 0);
data << uint32(qRew->RewardItemIdCount[i]);
}
}
SendPacket(&data);
}
void WorldSession::SendLfgBootPlayer(const LfgPlayerBoot* pBoot)
{
uint64 guid = GetPlayer()->GetGUID();
LfgAnswer playerVote = pBoot->votes.find(guid)->second;
uint8 votesNum = 0;
uint8 agreeNum = 0;
uint32 secsleft = uint8((pBoot->cancelTime - time(NULL)) / 1000);
for (LfgAnswerMap::const_iterator it = pBoot->votes.begin(); it != pBoot->votes.end(); ++it)
{
if (it->second != LFG_ANSWER_PENDING)
{
++votesNum;
if (it->second == LFG_ANSWER_AGREE)
++agreeNum;
}
}
sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_BOOT_PLAYER [" UI64FMTD "] inProgress: %u - didVote: %u - agree: %u - victim: [" UI64FMTD "] votes: %u - agrees: %u - left: %u - needed: %u - reason %s",
guid, uint8(pBoot->inProgress), uint8(playerVote != LFG_ANSWER_PENDING), uint8(playerVote == LFG_ANSWER_AGREE), pBoot->victim, votesNum, agreeNum, secsleft, pBoot->votedNeeded, pBoot->reason.c_str());
WorldPacket data(SMSG_LFG_BOOT_PLAYER, 1 + 1 + 1 + 8 + 4 + 4 + 4 + 4 + pBoot->reason.length());
data << uint8(pBoot->inProgress); // Vote in progress
data << uint8(playerVote != LFG_ANSWER_PENDING); // Did Vote
data << uint8(playerVote == LFG_ANSWER_AGREE); // Agree
data << uint64(pBoot->victim); // Victim GUID
data << uint32(votesNum); // Total Votes
data << uint32(agreeNum); // Agree Count
data << uint32(secsleft); // Time Left
data << uint32(pBoot->votedNeeded); // Needed Votes
data << pBoot->reason.c_str(); // Kick reason
SendPacket(&data);
}
void WorldSession::SendLfgUpdateProposal(uint32 proposalId, const LfgProposal* pProp)
{
if (!pProp)
return;
uint64 guid = GetPlayer()->GetGUID();
LfgProposalPlayerMap::const_iterator itPlayer = pProp->players.find(guid);
if (itPlayer == pProp->players.end()) // Player MUST be in the proposal
return;
LfgProposalPlayer* ppPlayer = itPlayer->second;
uint32 pLowGroupGuid = ppPlayer->groupLowGuid;
uint32 dLowGuid = pProp->groupLowGuid;
uint32 dungeonId = pProp->dungeonId;
bool isSameDungeon = false;
bool isContinue = false;
Group* grp = dLowGuid ? sGroupMgr->GetGroupByGUID(dLowGuid) : NULL;
uint32 completedEncounters = 0;
if (grp)
{
uint64 gguid = grp->GetGUID();
isContinue = grp->isLFGGroup() && sLFGMgr->GetState(gguid) != LFG_STATE_FINISHED_DUNGEON;
isSameDungeon = GetPlayer()->GetGroup() == grp && isContinue;
}
sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_PROPOSAL_UPDATE [" UI64FMTD "] state: %u", GetPlayer()->GetGUID(), pProp->state);
WorldPacket data(SMSG_LFG_PROPOSAL_UPDATE, 4 + 1 + 4 + 4 + 1 + 1 + pProp->players.size() * (4 + 1 + 1 + 1 + 1 +1));
if (!isContinue) // Only show proposal dungeon if it's continue
{
LfgDungeonSet playerDungeons = sLFGMgr->GetSelectedDungeons(guid);
if (playerDungeons.size() == 1)
dungeonId = (*playerDungeons.begin());
}
if (LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(dungeonId))
{
dungeonId = dungeon->Entry();
// Select a player inside to be get completed encounters from
if (grp)
{
for (GroupReference* itr = grp->GetFirstMember(); itr != NULL; itr = itr->next())
{
Player* groupMember = itr->getSource();
if (groupMember && groupMember->GetMapId() == uint32(dungeon->map))
{
if (InstanceScript* instance = groupMember->GetInstanceScript())
completedEncounters = instance->GetCompletedEncounterMask();
break;
}
}
}
}
data << uint32(dungeonId); // Dungeon
data << uint8(pProp->state); // Result state
data << uint32(proposalId); // Internal Proposal ID
data << uint32(completedEncounters); // Bosses killed
data << uint8(isSameDungeon); // Silent (show client window)
data << uint8(pProp->players.size()); // Group size
for (itPlayer = pProp->players.begin(); itPlayer != pProp->players.end(); ++itPlayer)
{
ppPlayer = itPlayer->second;
data << uint32(ppPlayer->role); // Role
data << uint8(itPlayer->first == guid); // Self player
if (!ppPlayer->groupLowGuid) // Player not it a group
{
data << uint8(0); // Not in dungeon
data << uint8(0); // Not same group
}
else
{
data << uint8(ppPlayer->groupLowGuid == dLowGuid); // In dungeon (silent)
data << uint8(ppPlayer->groupLowGuid == pLowGroupGuid); // Same Group than player
}
data << uint8(ppPlayer->accept != LFG_ANSWER_PENDING); // Answered
data << uint8(ppPlayer->accept == LFG_ANSWER_AGREE); // Accepted
}
SendPacket(&data);
}
void WorldSession::SendLfgUpdateSearch(bool update)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_UPDATE_SEARCH [" UI64FMTD "] update: %u", GetPlayer()->GetGUID(), update ? 1 : 0);
WorldPacket data(SMSG_LFG_UPDATE_SEARCH, 1);
data << uint8(update); // In Lfg Queue?
SendPacket(&data);
}
void WorldSession::SendLfgDisabled()
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_DISABLED [" UI64FMTD "]", GetPlayer()->GetGUID());
WorldPacket data(SMSG_LFG_DISABLED, 0);
SendPacket(&data);
}
void WorldSession::SendLfgOfferContinue(uint32 dungeonEntry)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_OFFER_CONTINUE [" UI64FMTD "] dungeon entry: %u", GetPlayer()->GetGUID(), dungeonEntry);
WorldPacket data(SMSG_LFG_OFFER_CONTINUE, 4);
data << uint32(dungeonEntry);
SendPacket(&data);
}
void WorldSession::SendLfgTeleportError(uint8 err)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_TELEPORT_DENIED [" UI64FMTD "] reason: %u", GetPlayer()->GetGUID(), err);
WorldPacket data(SMSG_LFG_TELEPORT_DENIED, 4);
data << uint32(err); // Error
SendPacket(&data);
}
/*
void WorldSession::SendLfrUpdateListOpcode(uint32 dungeonEntry)
{
sLog->outDebug(LOG_FILTER_PACKETIO, "SMSG_UPDATE_LFG_LIST [" UI64FMTD "] dungeon entry: %u", GetPlayer()->GetGUID(), dungeonEntry);
WorldPacket data(SMSG_UPDATE_LFG_LIST);
SendPacket(&data);
}
*/
+508
View File
@@ -0,0 +1,508 @@
/*
* Copyright (C) 2008-2012 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 "WorldPacket.h"
#include "Log.h"
#include "Corpse.h"
#include "GameObject.h"
#include "Player.h"
#include "ObjectAccessor.h"
#include "WorldSession.h"
#include "LootMgr.h"
#include "Object.h"
#include "Group.h"
#include "World.h"
#include "Util.h"
void WorldSession::HandleAutostoreLootItemOpcode(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_AUTOSTORE_LOOT_ITEM");
Player* player = GetPlayer();
uint64 lguid = player->GetLootGUID();
Loot* loot = NULL;
uint8 lootSlot = 0;
recv_data >> lootSlot;
if (IS_GAMEOBJECT_GUID(lguid))
{
GameObject* go = player->GetMap()->GetGameObject(lguid);
// not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO
if (!go || ((go->GetOwnerGUID() != _player->GetGUID() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player, INTERACTION_DISTANCE)))
{
player->SendLootRelease(lguid);
return;
}
loot = &go->loot;
}
else if (IS_ITEM_GUID(lguid))
{
Item* pItem = player->GetItemByGuid(lguid);
if (!pItem)
{
player->SendLootRelease(lguid);
return;
}
loot = &pItem->loot;
}
else if (IS_CORPSE_GUID(lguid))
{
Corpse* bones = ObjectAccessor::GetCorpse(*player, lguid);
if (!bones)
{
player->SendLootRelease(lguid);
return;
}
loot = &bones->loot;
}
else
{
Creature* creature = GetPlayer()->GetMap()->GetCreature(lguid);
bool ok_loot = creature && creature->isAlive() == (player->getClass() == CLASS_ROGUE && creature->lootForPickPocketed);
if (!ok_loot || !creature->IsWithinDistInMap(_player, INTERACTION_DISTANCE))
{
player->SendLootRelease(lguid);
return;
}
loot = &creature->loot;
}
player->StoreLootItem(lootSlot, loot);
}
void WorldSession::HandleLootMoneyOpcode(WorldPacket & /*recv_data*/)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_LOOT_MONEY");
Player* player = GetPlayer();
uint64 guid = player->GetLootGUID();
if (!guid)
return;
Loot* loot = NULL;
bool shareMoney = true;
switch (GUID_HIPART(guid))
{
case HIGHGUID_GAMEOBJECT:
{
GameObject* go = GetPlayer()->GetMap()->GetGameObject(guid);
// do not check distance for GO if player is the owner of it (ex. fishing bobber)
if (go && ((go->GetOwnerGUID() == player->GetGUID() || go->IsWithinDistInMap(player, INTERACTION_DISTANCE))))
loot = &go->loot;
break;
}
case HIGHGUID_CORPSE: // remove insignia ONLY in BG
{
Corpse* bones = ObjectAccessor::GetCorpse(*player, guid);
if (bones && bones->IsWithinDistInMap(player, INTERACTION_DISTANCE))
{
loot = &bones->loot;
shareMoney = false;
}
break;
}
case HIGHGUID_ITEM:
{
if (Item* item = player->GetItemByGuid(guid))
{
loot = &item->loot;
shareMoney = false;
}
break;
}
case HIGHGUID_UNIT:
case HIGHGUID_VEHICLE:
{
Creature* creature = player->GetMap()->GetCreature(guid);
bool lootAllowed = creature && creature->isAlive() == (player->getClass() == CLASS_ROGUE && creature->lootForPickPocketed);
if (lootAllowed && creature->IsWithinDistInMap(player, INTERACTION_DISTANCE))
{
loot = &creature->loot;
if (creature->isAlive())
shareMoney = false;
}
break;
}
default:
return; // unlootable type
}
if (loot)
{
loot->NotifyMoneyRemoved();
if (shareMoney && player->GetGroup()) //item, pickpocket and players can be looted only single player
{
Group* group = player->GetGroup();
std::vector<Player*> playersNear;
for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
{
Player* member = itr->getSource();
if (!member)
continue;
if (player->IsWithinDistInMap(member, sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE), false))
playersNear.push_back(member);
}
uint32 goldPerPlayer = uint32((loot->gold) / (playersNear.size()));
for (std::vector<Player*>::const_iterator i = playersNear.begin(); i != playersNear.end(); ++i)
{
(*i)->ModifyMoney(goldPerPlayer);
(*i)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY, goldPerPlayer);
WorldPacket data(SMSG_LOOT_MONEY_NOTIFY, 4 + 1);
data << uint32(goldPerPlayer);
data << uint8(playersNear.size() > 1 ? 0 : 1); // Controls the text displayed in chat. 0 is "Your share is..." and 1 is "You loot..."
(*i)->GetSession()->SendPacket(&data);
}
}
else
{
player->ModifyMoney(loot->gold);
player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY, loot->gold);
WorldPacket data(SMSG_LOOT_MONEY_NOTIFY, 4 + 1);
data << uint32(loot->gold);
data << uint8(1); // "You loot..."
SendPacket(&data);
}
loot->gold = 0;
}
}
void WorldSession::HandleLootOpcode(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_LOOT");
uint64 guid;
recv_data >> guid;
// Check possible cheat
if (!_player->isAlive())
return;
GetPlayer()->SendLoot(guid, LOOT_CORPSE);
// interrupt cast
if (GetPlayer()->IsNonMeleeSpellCasted(false))
GetPlayer()->InterruptNonMeleeSpells(false);
}
void WorldSession::HandleLootReleaseOpcode(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_LOOT_RELEASE");
// cheaters can modify lguid to prevent correct apply loot release code and re-loot
// use internal stored guid
recv_data.read_skip<uint64>(); // guid;
if (uint64 lguid = GetPlayer()->GetLootGUID())
DoLootRelease(lguid);
}
void WorldSession::DoLootRelease(uint64 lguid)
{
Player *player = GetPlayer();
Loot *loot;
player->SetLootGUID(0);
player->SendLootRelease(lguid);
player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_LOOTING);
if (!player->IsInWorld())
return;
if (IS_GAMEOBJECT_GUID(lguid))
{
GameObject* go = GetPlayer()->GetMap()->GetGameObject(lguid);
// not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO
if (!go || ((go->GetOwnerGUID() != _player->GetGUID() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player, INTERACTION_DISTANCE)))
return;
loot = &go->loot;
if (go->GetGoType() == GAMEOBJECT_TYPE_DOOR)
{
// locked doors are opened with spelleffect openlock, prevent remove its as looted
go->UseDoorOrButton();
}
else if (loot->isLooted() || go->GetGoType() == GAMEOBJECT_TYPE_FISHINGNODE)
{
// GO is mineral vein? so it is not removed after its looted
if (go->GetGoType() == GAMEOBJECT_TYPE_CHEST)
{
uint32 go_min = go->GetGOInfo()->chest.minSuccessOpens;
uint32 go_max = go->GetGOInfo()->chest.maxSuccessOpens;
// only vein pass this check
if (go_min != 0 && go_max > go_min)
{
float amount_rate = sWorld->getRate(RATE_MINING_AMOUNT);
float min_amount = go_min*amount_rate;
float max_amount = go_max*amount_rate;
go->AddUse();
float uses = float(go->GetUseCount());
if (uses < max_amount)
{
if (uses >= min_amount)
{
float chance_rate = sWorld->getRate(RATE_MINING_NEXT);
int32 ReqValue = 175;
LockEntry const* lockInfo = sLockStore.LookupEntry(go->GetGOInfo()->chest.lockId);
if (lockInfo)
ReqValue = lockInfo->Skill[0];
float skill = float(player->GetSkillValue(SKILL_MINING))/(ReqValue+25);
double chance = pow(0.8*chance_rate, 4*(1/double(max_amount))*double(uses));
if (roll_chance_f((float)(100*chance+skill)))
{
go->SetLootState(GO_READY);
}
else // not have more uses
go->SetLootState(GO_JUST_DEACTIVATED);
}
else // 100% chance until min uses
go->SetLootState(GO_READY);
}
else // max uses already
go->SetLootState(GO_JUST_DEACTIVATED);
}
else // not vein
go->SetLootState(GO_JUST_DEACTIVATED);
}
else if (go->GetGoType() == GAMEOBJECT_TYPE_FISHINGHOLE)
{ // The fishing hole used once more
go->AddUse(); // if the max usage is reached, will be despawned in next tick
if (go->GetUseCount() >= urand(go->GetGOInfo()->fishinghole.minSuccessOpens, go->GetGOInfo()->fishinghole.maxSuccessOpens))
{
go->SetLootState(GO_JUST_DEACTIVATED);
}
else
go->SetLootState(GO_READY);
}
else // not chest (or vein/herb/etc)
go->SetLootState(GO_JUST_DEACTIVATED);
loot->clear();
}
else
{
// not fully looted object
go->SetLootState(GO_ACTIVATED, player);
// if the round robin player release, reset it.
if (player->GetGUID() == loot->roundRobinPlayer)
{
if (Group* group = player->GetGroup())
{
if (group->GetLootMethod() != MASTER_LOOT)
{
loot->roundRobinPlayer = 0;
}
}
else
loot->roundRobinPlayer = 0;
}
}
}
else if (IS_CORPSE_GUID(lguid)) // ONLY remove insignia at BG
{
Corpse* corpse = ObjectAccessor::GetCorpse(*player, lguid);
if (!corpse || !corpse->IsWithinDistInMap(_player, INTERACTION_DISTANCE))
return;
loot = &corpse->loot;
if (loot->isLooted())
{
loot->clear();
corpse->RemoveFlag(CORPSE_FIELD_DYNAMIC_FLAGS, CORPSE_DYNFLAG_LOOTABLE);
}
}
else if (IS_ITEM_GUID(lguid))
{
Item* pItem = player->GetItemByGuid(lguid);
if (!pItem)
return;
ItemTemplate const* proto = pItem->GetTemplate();
// destroy only 5 items from stack in case prospecting and milling
if (proto->Flags & (ITEM_PROTO_FLAG_PROSPECTABLE | ITEM_PROTO_FLAG_MILLABLE))
{
pItem->m_lootGenerated = false;
pItem->loot.clear();
uint32 count = pItem->GetCount();
// >=5 checked in spell code, but will work for cheating cases also with removing from another stacks.
if (count > 5)
count = 5;
player->DestroyItemCount(pItem, count, true);
}
else
// FIXME: item must not be deleted in case not fully looted state. But this pre-request implement loot saving in DB at item save. Or cheating possible.
player->DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), true);
return; // item can be looted only single player
}
else
{
Creature* creature = GetPlayer()->GetMap()->GetCreature(lguid);
bool ok_loot = creature && creature->isAlive() == (player->getClass() == CLASS_ROGUE && creature->lootForPickPocketed);
if (!ok_loot || !creature->IsWithinDistInMap(_player, INTERACTION_DISTANCE))
return;
loot = &creature->loot;
if (loot->isLooted())
{
// skip pickpocketing loot for speed, skinning timer reduction is no-op in fact
if (!creature->isAlive())
creature->AllLootRemovedFromCorpse();
creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
loot->clear();
}
else
{
// if the round robin player release, reset it.
if (player->GetGUID() == loot->roundRobinPlayer)
{
if (Group* group = player->GetGroup())
{
if (group->GetLootMethod() != MASTER_LOOT)
{
loot->roundRobinPlayer = 0;
group->SendLooter(creature, NULL);
// force update of dynamic flags, otherwise other group's players still not able to loot.
creature->ForceValuesUpdateAtIndex(UNIT_DYNAMIC_FLAGS);
}
}
else
loot->roundRobinPlayer = 0;
}
}
}
//Player is not looking at loot list, he doesn't need to see updates on the loot list
loot->RemoveLooter(player->GetGUID());
}
void WorldSession::HandleLootMasterGiveOpcode(WorldPacket & recv_data)
{
uint8 slotid;
uint64 lootguid, target_playerguid;
recv_data >> lootguid >> slotid >> target_playerguid;
if (!_player->GetGroup() || _player->GetGroup()->GetLooterGuid() != _player->GetGUID())
{
_player->SendLootRelease(GetPlayer()->GetLootGUID());
return;
}
Player* target = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(target_playerguid, 0, HIGHGUID_PLAYER));
if (!target)
return;
sLog->outDebug(LOG_FILTER_NETWORKIO, "WorldSession::HandleLootMasterGiveOpcode (CMSG_LOOT_MASTER_GIVE, 0x02A3) Target = [%s].", target->GetName());
if (_player->GetLootGUID() != lootguid)
return;
Loot* pLoot = NULL;
if (IS_CRE_OR_VEH_GUID(GetPlayer()->GetLootGUID()))
{
Creature* creature = GetPlayer()->GetMap()->GetCreature(lootguid);
if (!creature)
return;
pLoot = &creature->loot;
}
else if (IS_GAMEOBJECT_GUID(GetPlayer()->GetLootGUID()))
{
GameObject* pGO = GetPlayer()->GetMap()->GetGameObject(lootguid);
if (!pGO)
return;
pLoot = &pGO->loot;
}
if (!pLoot)
return;
if (slotid > pLoot->items.size())
{
sLog->outDebug(LOG_FILTER_LOOT, "MasterLootItem: Player %s might be using a hack! (slot %d, size %lu)", GetPlayer()->GetName(), slotid, (unsigned long)pLoot->items.size());
return;
}
LootItem& item = pLoot->items[slotid];
ItemPosCountVec dest;
InventoryResult msg = target->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, item.itemid, item.count);
if (msg != EQUIP_ERR_OK)
{
target->SendEquipError(msg, NULL, NULL, item.itemid);
// send duplicate of error massage to master looter
_player->SendEquipError(msg, NULL, NULL, item.itemid);
return;
}
// list of players allowed to receive this item in trade
AllowedLooterSet looters = item.GetAllowedLooters();
// not move item from loot to target inventory
Item* newitem = target->StoreNewItem(dest, item.itemid, true, item.randomPropertyId, looters);
target->SendNewItem(newitem, uint32(item.count), false, false, true);
target->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM, item.itemid, item.count);
target->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE, pLoot->loot_type, item.count);
target->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM, item.itemid, item.count);
// mark as looted
item.count=0;
item.is_looted=true;
pLoot->NotifyItemRemoved(slotid);
--pLoot->unlootedCount;
}
+773
View File
@@ -0,0 +1,773 @@
/*
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
*
* 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 "DatabaseEnv.h"
#include "Mail.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "Opcodes.h"
#include "Log.h"
#include "World.h"
#include "ObjectMgr.h"
#include "Player.h"
#include "Language.h"
#include "DBCStores.h"
#include "Item.h"
#include "AccountMgr.h"
void WorldSession::HandleSendMail(WorldPacket & recv_data)
{
uint64 mailbox, unk3;
std::string receiver, subject, body;
uint32 unk1, unk2, money, COD;
uint8 unk4;
recv_data >> mailbox;
recv_data >> receiver;
recv_data >> subject;
recv_data >> body;
recv_data >> unk1; // stationery?
recv_data >> unk2; // 0x00000000
uint8 items_count;
recv_data >> items_count; // attached items count
if (items_count > MAX_MAIL_ITEMS) // client limit
{
GetPlayer()->SendMailResult(0, MAIL_SEND, MAIL_ERR_TOO_MANY_ATTACHMENTS);
recv_data.rfinish(); // set to end to avoid warnings spam
return;
}
uint64 itemGUIDs[MAX_MAIL_ITEMS];
for (uint8 i = 0; i < items_count; ++i)
{
recv_data.read_skip<uint8>(); // item slot in mail, not used
recv_data >> itemGUIDs[i];
}
recv_data >> money >> COD; // money and cod
recv_data >> unk3; // const 0
recv_data >> unk4; // const 0
// packet read complete, now do check
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
return;
if (receiver.empty())
return;
Player* player = _player;
if (player->getLevel() < sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ))
{
SendNotification(GetTrinityString(LANG_MAIL_SENDER_REQ), sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ));
return;
}
uint64 rc = 0;
if (normalizePlayerName(receiver))
rc = sObjectMgr->GetPlayerGUIDByName(receiver);
if (!rc)
{
sLog->outDetail("Player %u is sending mail to %s (GUID: not existed!) with subject %s and body %s includes %u items, %u copper and %u COD copper with unk1 = %u, unk2 = %u",
player->GetGUIDLow(), receiver.c_str(), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2);
player->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_NOT_FOUND);
return;
}
sLog->outDetail("Player %u is sending mail to %s (GUID: %u) with subject %s and body %s includes %u items, %u copper and %u COD copper with unk1 = %u, unk2 = %u", player->GetGUIDLow(), receiver.c_str(), GUID_LOPART(rc), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2);
if (player->GetGUID() == rc)
{
player->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANNOT_SEND_TO_SELF);
return;
}
uint32 cost = items_count ? 30 * items_count : 30; // price hardcoded in client
uint32 reqmoney = cost + money;
if (!player->HasEnoughMoney(reqmoney))
{
player->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_ENOUGH_MONEY);
return;
}
Player* receive = ObjectAccessor::FindPlayer(rc);
uint32 rc_team = 0;
uint8 mails_count = 0; //do not allow to send to one player more than 100 mails
uint8 receiveLevel = 0;
if (receive)
{
rc_team = receive->GetTeam();
mails_count = receive->GetMailSize();
receiveLevel = receive->getLevel();
}
else
{
rc_team = sObjectMgr->GetPlayerTeamByGUID(rc);
if (QueryResult result = CharacterDatabase.PQuery("SELECT COUNT(*) FROM mail WHERE receiver = '%u'", GUID_LOPART(rc)))
{
Field* fields = result->Fetch();
mails_count = fields[0].GetUInt32();
}
if (QueryResult result = CharacterDatabase.PQuery("SELECT level FROM characters WHERE guid = '%u'", GUID_LOPART(rc)))
{
Field* fields = result->Fetch();
receiveLevel = fields[0].GetUInt8();
}
}
//do not allow to have more than 100 mails in mailbox.. mails count is in opcode uint8!!! - so max can be 255..
if (mails_count > 100)
{
player->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_CAP_REACHED);
return;
}
// test the receiver's Faction... or all items are account bound
bool accountBound = items_count ? true : false;
for (uint8 i = 0; i < items_count; ++i)
{
Item* item = player->GetItemByGuid(itemGUIDs[i]);
if (item)
{
ItemTemplate const* itemProto = item->GetTemplate();
if (!itemProto || !(itemProto->Flags & ITEM_PROTO_FLAG_BIND_TO_ACCOUNT))
{
accountBound = false;
break;
}
}
}
if (!accountBound && !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL) && player->GetTeam() != rc_team && AccountMgr::IsPlayerAccount(GetSecurity()))
{
player->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_YOUR_TEAM);
return;
}
if (receiveLevel < sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ))
{
SendNotification(GetTrinityString(LANG_MAIL_RECEIVER_REQ), sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ));
return;
}
uint32 rc_account = receive
? receive->GetSession()->GetAccountId()
: sObjectMgr->GetPlayerAccountIdByGUID(rc);
Item* items[MAX_MAIL_ITEMS];
for (uint8 i = 0; i < items_count; ++i)
{
if (!itemGUIDs[i])
{
player->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID);
return;
}
Item* item = player->GetItemByGuid(itemGUIDs[i]);
// prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail)
if (!item)
{
player->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID);
return;
}
if (!item->CanBeTraded(true))
{
player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM);
return;
}
if (item->IsBoundAccountWide() && item->IsSoulBound() && player->GetSession()->GetAccountId() != rc_account)
{
player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_ARTEFACTS_ONLY_FOR_OWN_CHARACTERS);
return;
}
if (item->GetTemplate()->Flags & ITEM_PROTO_FLAG_CONJURED || item->GetUInt32Value(ITEM_FIELD_DURATION))
{
player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM);
return;
}
if (COD && item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED))
{
player->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANT_SEND_WRAPPED_COD);
return;
}
if (item->IsNotEmptyBag())
{
player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_CAN_ONLY_DO_WITH_EMPTY_BAGS);
return;
}
items[i] = item;
}
player->SendMailResult(0, MAIL_SEND, MAIL_OK);
player->ModifyMoney(-int32(reqmoney));
player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL, cost);
bool needItemDelay = false;
MailDraft draft(subject, body);
SQLTransaction trans = CharacterDatabase.BeginTransaction();
if (items_count > 0 || money > 0)
{
if (items_count > 0)
{
for (uint8 i = 0; i < items_count; ++i)
{
Item* item = items[i];
if (!AccountMgr::IsPlayerAccount(GetSecurity()) && sWorld->getBoolConfig(CONFIG_GM_LOG_TRADE))
{
sLog->outCommand(GetAccountId(), "GM %s (Account: %u) mail item: %s (Entry: %u Count: %u) to player: %s (Account: %u)",
GetPlayerName(), GetAccountId(), item->GetTemplate()->Name1.c_str(), item->GetEntry(), item->GetCount(), receiver.c_str(), rc_account);
}
item->SetNotRefundable(GetPlayer()); // makes the item no longer refundable
player->MoveItemFromInventory(items[i]->GetBagSlot(), item->GetSlot(), true);
item->DeleteFromInventoryDB(trans); // deletes item from character's inventory
item->SetOwnerGUID(rc);
item->SaveToDB(trans); // recursive and not have transaction guard into self, item not in inventory and can be save standalone
draft.AddItem(item);
}
// if item send to character at another account, then apply item delivery delay
needItemDelay = player->GetSession()->GetAccountId() != rc_account;
}
if (money > 0 && !AccountMgr::IsPlayerAccount(GetSecurity()) && sWorld->getBoolConfig(CONFIG_GM_LOG_TRADE))
{
sLog->outCommand(GetAccountId(), "GM %s (Account: %u) mail money: %u to player: %s (Account: %u)",
GetPlayerName(), GetAccountId(), money, receiver.c_str(), rc_account);
}
}
// If theres is an item, there is a one hour delivery delay if sent to another account's character.
uint32 deliver_delay = needItemDelay ? sWorld->getIntConfig(CONFIG_MAIL_DELIVERY_DELAY) : 0;
// will delete item or place to receiver mail list
draft
.AddMoney(money)
.AddCOD(COD)
.SendMailTo(trans, MailReceiver(receive, GUID_LOPART(rc)), MailSender(player), body.empty() ? MAIL_CHECK_MASK_COPIED : MAIL_CHECK_MASK_HAS_BODY, deliver_delay);
player->SaveInventoryAndGoldToDB(trans);
CharacterDatabase.CommitTransaction(trans);
}
//called when mail is read
void WorldSession::HandleMailMarkAsRead(WorldPacket & recv_data)
{
uint64 mailbox;
uint32 mailId;
recv_data >> mailbox;
recv_data >> mailId;
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
return;
Player* player = _player;
Mail* m = player->GetMail(mailId);
if (m)
{
if (player->unReadMails)
--player->unReadMails;
m->checked = m->checked | MAIL_CHECK_MASK_READ;
player->m_mailsUpdated = true;
m->state = MAIL_STATE_CHANGED;
}
}
//called when client deletes mail
void WorldSession::HandleMailDelete(WorldPacket & recv_data)
{
uint64 mailbox;
uint32 mailId;
recv_data >> mailbox;
recv_data >> mailId;
recv_data.read_skip<uint32>(); // mailTemplateId
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
return;
Mail* m = _player->GetMail(mailId);
Player* player = _player;
player->m_mailsUpdated = true;
if (m)
{
// delete shouldn't show up for COD mails
if (m->COD)
{
player->SendMailResult(mailId, MAIL_DELETED, MAIL_ERR_INTERNAL_ERROR);
return;
}
m->state = MAIL_STATE_DELETED;
}
player->SendMailResult(mailId, MAIL_DELETED, MAIL_OK);
}
void WorldSession::HandleMailReturnToSender(WorldPacket & recv_data)
{
uint64 mailbox;
uint32 mailId;
recv_data >> mailbox;
recv_data >> mailId;
recv_data.read_skip<uint64>(); // original sender GUID for return to, not used
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
return;
Player* player = _player;
Mail* m = player->GetMail(mailId);
if (!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL))
{
player->SendMailResult(mailId, MAIL_RETURNED_TO_SENDER, MAIL_ERR_INTERNAL_ERROR);
return;
}
//we can return mail now
//so firstly delete the old one
SQLTransaction trans = CharacterDatabase.BeginTransaction();
trans->PAppend("DELETE FROM mail WHERE id = '%u'", mailId); // needed?
trans->PAppend("DELETE FROM mail_items WHERE mail_id = '%u'", mailId);
player->RemoveMail(mailId);
// only return mail if the player exists (and delete if not existing)
if (m->messageType == MAIL_NORMAL && m->sender)
{
MailDraft draft(m->subject, m->body);
if (m->mailTemplateId)
draft = MailDraft(m->mailTemplateId, false); // items already included
if (m->HasItems())
{
for (MailItemInfoVec::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
{
Item* item = player->GetMItem(itr2->item_guid);
if (item)
draft.AddItem(item);
else
{
//WTF?
}
player->RemoveMItem(itr2->item_guid);
}
}
draft.AddMoney(m->money).SendReturnToSender(GetAccountId(), m->receiver, m->sender, trans);
}
CharacterDatabase.CommitTransaction(trans);
delete m; //we can deallocate old mail
player->SendMailResult(mailId, MAIL_RETURNED_TO_SENDER, MAIL_OK);
}
//called when player takes item attached in mail
void WorldSession::HandleMailTakeItem(WorldPacket & recv_data)
{
uint64 mailbox;
uint32 mailId;
uint32 itemId;
recv_data >> mailbox;
recv_data >> mailId;
recv_data >> itemId; // item guid low
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
return;
Player* player = _player;
Mail* m = player->GetMail(mailId);
if (!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL))
{
player->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_INTERNAL_ERROR);
return;
}
// prevent cheating with skip client money check
if (!player->HasEnoughMoney(m->COD))
{
player->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_NOT_ENOUGH_MONEY);
return;
}
Item* it = player->GetMItem(itemId);
ItemPosCountVec dest;
uint8 msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, dest, it, false);
if (msg == EQUIP_ERR_OK)
{
SQLTransaction trans = CharacterDatabase.BeginTransaction();
m->RemoveItem(itemId);
m->removedItems.push_back(itemId);
if (m->COD > 0) //if there is COD, take COD money from player and send them to sender by mail
{
uint64 sender_guid = MAKE_NEW_GUID(m->sender, 0, HIGHGUID_PLAYER);
Player* receive = ObjectAccessor::FindPlayer(sender_guid);
uint32 sender_accId = 0;
if (!AccountMgr::IsPlayerAccount(GetSecurity()) && sWorld->getBoolConfig(CONFIG_GM_LOG_TRADE))
{
std::string sender_name;
if (receive)
{
sender_accId = receive->GetSession()->GetAccountId();
sender_name = receive->GetName();
}
else
{
// can be calculated early
sender_accId = sObjectMgr->GetPlayerAccountIdByGUID(sender_guid);
if (!sObjectMgr->GetPlayerNameByGUID(sender_guid, sender_name))
sender_name = sObjectMgr->GetTrinityStringForDBCLocale(LANG_UNKNOWN);
}
sLog->outCommand(GetAccountId(), "GM %s (Account: %u) receive mail item: %s (Entry: %u Count: %u) and send COD money: %u to player: %s (Account: %u)",
GetPlayerName(), GetAccountId(), it->GetTemplate()->Name1.c_str(), it->GetEntry(), it->GetCount(), m->COD, sender_name.c_str(), sender_accId);
}
else if (!receive)
sender_accId = sObjectMgr->GetPlayerAccountIdByGUID(sender_guid);
// check player existence
if (receive || sender_accId)
{
MailDraft(m->subject, "")
.AddMoney(m->COD)
.SendMailTo(trans, MailReceiver(receive, m->sender), MailSender(MAIL_NORMAL, m->receiver), MAIL_CHECK_MASK_COD_PAYMENT);
}
player->ModifyMoney(-int32(m->COD));
}
m->COD = 0;
m->state = MAIL_STATE_CHANGED;
player->m_mailsUpdated = true;
player->RemoveMItem(it->GetGUIDLow());
uint32 count = it->GetCount(); // save counts before store and possible merge with deleting
player->MoveItemToInventory(dest, it, true);
player->SaveInventoryAndGoldToDB(trans);
player->_SaveMail(trans);
CharacterDatabase.CommitTransaction(trans);
player->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_OK, 0, itemId, count);
}
else
player->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_EQUIP_ERROR, msg);
}
void WorldSession::HandleMailTakeMoney(WorldPacket & recv_data)
{
uint64 mailbox;
uint32 mailId;
recv_data >> mailbox;
recv_data >> mailId;
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
return;
Player* player = _player;
Mail* m = player->GetMail(mailId);
if (!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL))
{
player->SendMailResult(mailId, MAIL_MONEY_TAKEN, MAIL_ERR_INTERNAL_ERROR);
return;
}
player->SendMailResult(mailId, MAIL_MONEY_TAKEN, MAIL_OK);
player->ModifyMoney(m->money);
m->money = 0;
m->state = MAIL_STATE_CHANGED;
player->m_mailsUpdated = true;
// save money and mail to prevent cheating
SQLTransaction trans = CharacterDatabase.BeginTransaction();
player->SaveGoldToDB(trans);
player->_SaveMail(trans);
CharacterDatabase.CommitTransaction(trans);
}
//called when player lists his received mails
void WorldSession::HandleGetMailList(WorldPacket & recv_data)
{
uint64 mailbox;
recv_data >> mailbox;
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
return;
Player* player = _player;
//load players mails, and mailed items
if (!player->m_mailsLoaded)
player ->_LoadMail();
// client can't work with packets > max int16 value
const uint32 maxPacketSize = 32767;
uint32 mailsCount = 0; // real send to client mails amount
uint32 realCount = 0; // real mails amount
WorldPacket data(SMSG_MAIL_LIST_RESULT, (200)); // guess size
data << uint32(0); // real mail's count
data << uint8(0); // mail's count
time_t cur_time = time(NULL);
for (PlayerMails::iterator itr = player->GetMailBegin(); itr != player->GetMailEnd(); ++itr)
{
// packet send mail count as uint8, prevent overflow
if (mailsCount >= 254)
{
realCount += 1;
continue;
}
// skip deleted or not delivered (deliver delay not expired) mails
if ((*itr)->state == MAIL_STATE_DELETED || cur_time < (*itr)->deliver_time)
continue;
uint8 item_count = (*itr)->items.size(); // max count is MAX_MAIL_ITEMS (12)
size_t next_mail_size = 2+4+1+((*itr)->messageType == MAIL_NORMAL ? 8 : 4)+4*8+((*itr)->subject.size()+1)+((*itr)->body.size()+1)+1+item_count*(1+4+4+7*3*4+4+4+4+4+4+4+1);
if (data.wpos()+next_mail_size > maxPacketSize)
{
realCount += 1;
continue;
}
data << uint16(next_mail_size); // Message size
data << uint32((*itr)->messageID); // Message ID
data << uint8((*itr)->messageType); // Message Type
switch ((*itr)->messageType)
{
case MAIL_NORMAL: // sender guid
data << uint64(MAKE_NEW_GUID((*itr)->sender, 0, HIGHGUID_PLAYER));
break;
case MAIL_CREATURE:
case MAIL_GAMEOBJECT:
case MAIL_AUCTION:
data << uint32((*itr)->sender); // creature/gameobject entry, auction id
break;
case MAIL_ITEM: // item entry (?) sender = "Unknown", NYI
data << uint32(0); // item entry
break;
}
data << uint32((*itr)->COD); // COD
data << uint32(0); // probably changed in 3.3.3
data << uint32((*itr)->stationery); // stationery (Stationery.dbc)
data << uint32((*itr)->money); // Gold
data << uint32((*itr)->checked); // flags
data << float(((*itr)->expire_time-time(NULL))/DAY); // Time
data << uint32((*itr)->mailTemplateId); // mail template (MailTemplate.dbc)
data << (*itr)->subject; // Subject string - once 00, when mail type = 3, max 256
data << (*itr)->body; // message? max 8000
data << uint8(item_count); // client limit is 0x10
for (uint8 i = 0; i < item_count; ++i)
{
Item* item = player->GetMItem((*itr)->items[i].item_guid);
// item index (0-6?)
data << uint8(i);
// item guid low?
data << uint32((item ? item->GetGUIDLow() : 0));
// entry
data << uint32((item ? item->GetEntry() : 0));
for (uint8 j = 0; j < MAX_INSPECTED_ENCHANTMENT_SLOT; ++j)
{
data << uint32((item ? item->GetEnchantmentId((EnchantmentSlot)j) : 0));
data << uint32((item ? item->GetEnchantmentDuration((EnchantmentSlot)j) : 0));
data << uint32((item ? item->GetEnchantmentCharges((EnchantmentSlot)j) : 0));
}
// can be negative
data << int32((item ? item->GetItemRandomPropertyId() : 0));
// unk
data << uint32((item ? item->GetItemSuffixFactor() : 0));
// stack count
data << uint32((item ? item->GetCount() : 0));
// charges
data << uint32((item ? item->GetSpellCharges() : 0));
// durability
data << uint32((item ? item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY) : 0));
// durability
data << uint32((item ? item->GetUInt32Value(ITEM_FIELD_DURABILITY) : 0));
// unknown wotlk
data << uint8(0);
}
realCount += 1;
mailsCount += 1;
}
data.put<uint32>(0, realCount); // this will display warning about undelivered mail to player if realCount > mailsCount
data.put<uint8>(4, mailsCount); // set real send mails to client
SendPacket(&data);
// recalculate m_nextMailDelivereTime and unReadMails
_player->UpdateNextMailTimeAndUnreads();
}
//used when player copies mail body to his inventory
void WorldSession::HandleMailCreateTextItem(WorldPacket & recv_data)
{
uint64 mailbox;
uint32 mailId;
recv_data >> mailbox;
recv_data >> mailId;
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
return;
Player* player = _player;
Mail* m = player->GetMail(mailId);
if (!m || (m->body.empty() && !m->mailTemplateId) || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL))
{
player->SendMailResult(mailId, MAIL_MADE_PERMANENT, MAIL_ERR_INTERNAL_ERROR);
return;
}
Item* bodyItem = new Item; // This is not bag and then can be used new Item.
if (!bodyItem->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_ITEM), MAIL_BODY_ITEM_TEMPLATE, player))
{
delete bodyItem;
return;
}
// in mail template case we need create new item text
if (m->mailTemplateId)
{
MailTemplateEntry const* mailTemplateEntry = sMailTemplateStore.LookupEntry(m->mailTemplateId);
if (!mailTemplateEntry)
{
player->SendMailResult(mailId, MAIL_MADE_PERMANENT, MAIL_ERR_INTERNAL_ERROR);
return;
}
bodyItem->SetText(mailTemplateEntry->content[GetSessionDbcLocale()]);
}
else
bodyItem->SetText(m->body);
bodyItem->SetUInt32Value(ITEM_FIELD_CREATOR, m->sender);
bodyItem->SetFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_MAIL_TEXT_MASK);
sLog->outDetail("HandleMailCreateTextItem mailid=%u", mailId);
ItemPosCountVec dest;
uint8 msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, dest, bodyItem, false);
if (msg == EQUIP_ERR_OK)
{
m->checked = m->checked | MAIL_CHECK_MASK_COPIED;
m->state = MAIL_STATE_CHANGED;
player->m_mailsUpdated = true;
player->StoreItem(dest, bodyItem, true);
player->SendMailResult(mailId, MAIL_MADE_PERMANENT, MAIL_OK);
}
else
{
player->SendMailResult(mailId, MAIL_MADE_PERMANENT, MAIL_ERR_EQUIP_ERROR, msg);
delete bodyItem;
}
}
//TODO Fix me! ... this void has probably bad condition, but good data are sent
void WorldSession::HandleQueryNextMailTime(WorldPacket & /*recv_data*/)
{
WorldPacket data(MSG_QUERY_NEXT_MAIL_TIME, 8);
if (!_player->m_mailsLoaded)
_player->_LoadMail();
if (_player->unReadMails > 0)
{
data << uint32(0); // float
data << uint32(0); // count
uint32 count = 0;
time_t now = time(NULL);
for (PlayerMails::iterator itr = _player->GetMailBegin(); itr != _player->GetMailEnd(); ++itr)
{
Mail* m = (*itr);
// must be not checked yet
if (m->checked & MAIL_CHECK_MASK_READ)
continue;
// and already delivered
if (now < m->deliver_time)
continue;
if (m->messageType)
data << uint64(m->sender); // player guid
else
data << uint32(m->sender); // creature entry
switch (m->messageType)
{
case MAIL_AUCTION:
data << uint32(2);
data << uint32(2);
data << uint32(m->stationery);
break;
default:
data << uint32(0);
data << uint32(0);
data << uint32(m->stationery);
break;
}
data << uint32(0xC6000000); // float unk, time or something
++count;
if (count == 2) // do not display more than 2 mails
break;
}
data.put<uint32>(4, count);
}
else
{
data << uint32(0xC7A8C000);
data << uint32(0x00000000);
}
SendPacket(&data);
}
File diff suppressed because it is too large Load Diff
+573
View File
@@ -0,0 +1,573 @@
/*
* Copyright (C) 2008-2012 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 "WorldPacket.h"
#include "WorldSession.h"
#include "Opcodes.h"
#include "Log.h"
#include "Corpse.h"
#include "Player.h"
#include "SpellAuras.h"
#include "MapManager.h"
#include "Transport.h"
#include "Battleground.h"
#include "WaypointMovementGenerator.h"
#include "InstanceSaveMgr.h"
#include "ObjectMgr.h"
void WorldSession::HandleMoveWorldportAckOpcode(WorldPacket & /*recv_data*/)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: got MSG_MOVE_WORLDPORT_ACK.");
HandleMoveWorldportAckOpcode();
}
void WorldSession::HandleMoveWorldportAckOpcode()
{
// ignore unexpected far teleports
if (!GetPlayer()->IsBeingTeleportedFar())
return;
GetPlayer()->SetSemaphoreTeleportFar(false);
// get the teleport destination
WorldLocation &loc = GetPlayer()->GetTeleportDest();
// possible errors in the coordinate validity check
if (!MapManager::IsValidMapCoord(loc))
{
LogoutPlayer(false);
return;
}
// get the destination map entry, not the current one, this will fix homebind and reset greeting
MapEntry const* mEntry = sMapStore.LookupEntry(loc.GetMapId());
InstanceTemplate const* mInstance = sObjectMgr->GetInstanceTemplate(loc.GetMapId());
// reset instance validity, except if going to an instance inside an instance
if (GetPlayer()->m_InstanceValid == false && !mInstance)
GetPlayer()->m_InstanceValid = true;
Map* oldMap = GetPlayer()->GetMap();
ASSERT(oldMap);
if (GetPlayer()->IsInWorld())
{
sLog->outCrash("Player (Name %s) is still in world when teleported from map %u to new map %u", GetPlayer()->GetName(), oldMap->GetId(), loc.GetMapId());
oldMap->RemovePlayerFromMap(GetPlayer(), false);
}
// relocate the player to the teleport destination
Map* newMap = sMapMgr->CreateMap(loc.GetMapId(), GetPlayer());
// the CanEnter checks are done in TeleporTo but conditions may change
// while the player is in transit, for example the map may get full
if (!newMap || !newMap->CanEnter(GetPlayer()))
{
sLog->outError("Map %d could not be created for player %d, porting player to homebind", loc.GetMapId(), GetPlayer()->GetGUIDLow());
GetPlayer()->TeleportTo(GetPlayer()->m_homebindMapId, GetPlayer()->m_homebindX, GetPlayer()->m_homebindY, GetPlayer()->m_homebindZ, GetPlayer()->GetOrientation());
return;
}
else
GetPlayer()->Relocate(&loc);
GetPlayer()->ResetMap();
GetPlayer()->SetMap(newMap);
GetPlayer()->SendInitialPacketsBeforeAddToMap();
if (!GetPlayer()->GetMap()->AddPlayerToMap(GetPlayer()))
{
sLog->outError("WORLD: failed to teleport player %s (%d) to map %d because of unknown reason!", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow(), loc.GetMapId());
GetPlayer()->ResetMap();
GetPlayer()->SetMap(oldMap);
GetPlayer()->TeleportTo(GetPlayer()->m_homebindMapId, GetPlayer()->m_homebindX, GetPlayer()->m_homebindY, GetPlayer()->m_homebindZ, GetPlayer()->GetOrientation());
return;
}
// battleground state prepare (in case join to BG), at relogin/tele player not invited
// only add to bg group and object, if the player was invited (else he entered through command)
if (_player->InBattleground())
{
// cleanup setting if outdated
if (!mEntry->IsBattlegroundOrArena())
{
// We're not in BG
_player->SetBattlegroundId(0, BATTLEGROUND_TYPE_NONE);
// reset destination bg team
_player->SetBGTeam(0);
}
// join to bg case
else if (Battleground* bg = _player->GetBattleground())
{
if (_player->IsInvitedForBattlegroundInstance(_player->GetBattlegroundId()))
bg->AddPlayer(_player);
}
}
GetPlayer()->SendInitialPacketsAfterAddToMap();
// flight fast teleport case
if (GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE)
{
if (!_player->InBattleground())
{
// short preparations to continue flight
FlightPathMovementGenerator* flight = (FlightPathMovementGenerator*)(GetPlayer()->GetMotionMaster()->top());
flight->Initialize(*GetPlayer());
return;
}
// battleground state prepare, stop flight
GetPlayer()->GetMotionMaster()->MovementExpired();
GetPlayer()->CleanupAfterTaxiFlight();
}
// resurrect character at enter into instance where his corpse exist after add to map
Corpse* corpse = GetPlayer()->GetCorpse();
if (corpse && corpse->GetType() != CORPSE_BONES && corpse->GetMapId() == GetPlayer()->GetMapId())
{
if (mEntry->IsDungeon())
{
GetPlayer()->ResurrectPlayer(0.5f, false);
GetPlayer()->SpawnCorpseBones();
}
}
bool allowMount = !mEntry->IsDungeon() || mEntry->IsBattlegroundOrArena();
if (mInstance)
{
Difficulty diff = GetPlayer()->GetDifficulty(mEntry->IsRaid());
if (MapDifficulty const* mapDiff = GetMapDifficultyData(mEntry->MapID, diff))
{
if (mapDiff->resetTime)
{
if (time_t timeReset = sInstanceSaveMgr->GetResetTimeFor(mEntry->MapID, diff))
{
uint32 timeleft = uint32(timeReset - time(NULL));
GetPlayer()->SendInstanceResetWarning(mEntry->MapID, diff, timeleft);
}
}
}
allowMount = mInstance->AllowMount;
}
// mount allow check
if (!allowMount)
_player->RemoveAurasByType(SPELL_AURA_MOUNTED);
// update zone immediately, otherwise leave channel will cause crash in mtmap
uint32 newzone, newarea;
GetPlayer()->GetZoneAndAreaId(newzone, newarea);
GetPlayer()->UpdateZone(newzone, newarea);
// honorless target
if (GetPlayer()->pvpInfo.inHostileArea)
GetPlayer()->CastSpell(GetPlayer(), 2479, true);
// in friendly area
else if (GetPlayer()->IsPvP() && !GetPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP))
GetPlayer()->UpdatePvP(false, false);
// resummon pet
GetPlayer()->ResummonPetTemporaryUnSummonedIfAny();
//lets process all delayed operations on successful teleport
GetPlayer()->ProcessDelayedOperations();
}
void WorldSession::HandleMoveTeleportAck(WorldPacket& recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "MSG_MOVE_TELEPORT_ACK");
uint64 guid;
recv_data.readPackGUID(guid);
uint32 flags, time;
recv_data >> flags >> time;
sLog->outStaticDebug("Guid " UI64FMTD, guid);
sLog->outStaticDebug("Flags %u, time %u", flags, time/IN_MILLISECONDS);
Unit* mover = _player->m_mover;
Player* plMover = mover->GetTypeId() == TYPEID_PLAYER ? (Player*)mover : NULL;
if (!plMover || !plMover->IsBeingTeleportedNear())
return;
if (guid != plMover->GetGUID())
return;
plMover->SetSemaphoreTeleportNear(false);
uint32 old_zone = plMover->GetZoneId();
WorldLocation const& dest = plMover->GetTeleportDest();
plMover->UpdatePosition(dest, true);
uint32 newzone, newarea;
plMover->GetZoneAndAreaId(newzone, newarea);
plMover->UpdateZone(newzone, newarea);
// new zone
if (old_zone != newzone)
{
// honorless target
if (plMover->pvpInfo.inHostileArea)
plMover->CastSpell(plMover, 2479, true);
// in friendly area
else if (plMover->IsPvP() && !plMover->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP))
plMover->UpdatePvP(false, false);
}
// resummon pet
GetPlayer()->ResummonPetTemporaryUnSummonedIfAny();
//lets process all delayed operations on successful teleport
GetPlayer()->ProcessDelayedOperations();
}
void WorldSession::HandleMovementOpcodes(WorldPacket & recv_data)
{
uint16 opcode = recv_data.GetOpcode();
Unit* mover = _player->m_mover;
ASSERT(mover != NULL); // there must always be a mover
Player* plMover = mover->GetTypeId() == TYPEID_PLAYER ? (Player*)mover : NULL;
// ignore, waiting processing in WorldSession::HandleMoveWorldportAckOpcode and WorldSession::HandleMoveTeleportAck
if (plMover && plMover->IsBeingTeleported())
{
recv_data.rfinish(); // prevent warnings spam
return;
}
/* extract packet */
uint64 guid;
recv_data.readPackGUID(guid);
MovementInfo movementInfo;
movementInfo.guid = guid;
ReadMovementInfo(recv_data, &movementInfo);
recv_data.rfinish(); // prevent warnings spam
// prevent tampered movement data
if (guid != mover->GetGUID())
return;
if (!movementInfo.pos.IsPositionValid())
{
recv_data.rfinish(); // prevent warnings spam
return;
}
/* handle special cases */
if (movementInfo.flags & MOVEMENTFLAG_ONTRANSPORT)
{
// transports size limited
// (also received at zeppelin leave by some reason with t_* as absolute in continent coordinates, can be safely skipped)
if (movementInfo.t_pos.GetPositionX() > 50 || movementInfo.t_pos.GetPositionY() > 50 || movementInfo.t_pos.GetPositionZ() > 50)
{
recv_data.rfinish(); // prevent warnings spam
return;
}
if (!Trinity::IsValidMapCoord(movementInfo.pos.GetPositionX() + movementInfo.t_pos.GetPositionX(), movementInfo.pos.GetPositionY() + movementInfo.t_pos.GetPositionY(),
movementInfo.pos.GetPositionZ() + movementInfo.t_pos.GetPositionZ(), movementInfo.pos.GetOrientation() + movementInfo.t_pos.GetOrientation()))
{
recv_data.rfinish(); // prevent warnings spam
return;
}
// if we boarded a transport, add us to it
if (plMover && !plMover->GetTransport())
{
// elevators also cause the client to send MOVEMENTFLAG_ONTRANSPORT - just dismount if the guid can be found in the transport list
for (MapManager::TransportSet::const_iterator iter = sMapMgr->m_Transports.begin(); iter != sMapMgr->m_Transports.end(); ++iter)
{
if ((*iter)->GetGUID() == movementInfo.t_guid)
{
plMover->m_transport = (*iter);
(*iter)->AddPassenger(plMover);
break;
}
}
}
if (!mover->GetTransport() && !mover->GetVehicle())
{
GameObject* go = mover->GetMap()->GetGameObject(movementInfo.t_guid);
if (!go || go->GetGoType() != GAMEOBJECT_TYPE_TRANSPORT)
movementInfo.flags &= ~MOVEMENTFLAG_ONTRANSPORT;
}
}
else if (plMover && plMover->GetTransport()) // if we were on a transport, leave
{
plMover->m_transport->RemovePassenger(plMover);
plMover->m_transport = NULL;
movementInfo.t_pos.Relocate(0.0f, 0.0f, 0.0f, 0.0f);
movementInfo.t_time = 0;
movementInfo.t_seat = -1;
}
// fall damage generation (ignore in flight case that can be triggered also at lags in moment teleportation to another map).
if (opcode == MSG_MOVE_FALL_LAND && plMover && !plMover->isInFlight())
plMover->HandleFall(movementInfo);
if (plMover && ((movementInfo.flags & MOVEMENTFLAG_SWIMMING) != 0) != plMover->IsInWater())
{
// now client not include swimming flag in case jumping under water
plMover->SetInWater(!plMover->IsInWater() || plMover->GetBaseMap()->IsUnderWater(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY(), movementInfo.pos.GetPositionZ()));
}
/*----------------------*/
/* process position-change */
WorldPacket data(opcode, recv_data.size());
movementInfo.time = getMSTime();
movementInfo.guid = mover->GetGUID();
WriteMovementInfo(&data, &movementInfo);
mover->SendMessageToSet(&data, _player);
mover->m_movementInfo = movementInfo;
// this is almost never true (not sure why it is sometimes, but it is), normally use mover->IsVehicle()
if (mover->GetVehicle())
{
mover->SetOrientation(movementInfo.pos.GetOrientation());
return;
}
mover->UpdatePosition(movementInfo.pos);
if (plMover) // nothing is charmed, or player charmed
{
plMover->UpdateFallInformationIfNeed(movementInfo, opcode);
if (movementInfo.pos.GetPositionZ() < -500.0f)
{
if (!(plMover->InBattleground()
&& plMover->GetBattleground()
&& plMover->GetBattleground()->HandlePlayerUnderMap(_player)))
{
// NOTE: this is actually called many times while falling
// even after the player has been teleported away
// TODO: discard movement packets after the player is rooted
if (plMover->isAlive())
{
plMover->EnvironmentalDamage(DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth());
// player can be alive if GM/etc
// change the death state to CORPSE to prevent the death timer from
// starting in the next player update
if (!plMover->isAlive())
plMover->KillPlayer();
}
}
}
}
}
void WorldSession::HandleForceSpeedChangeAck(WorldPacket &recv_data)
{
uint32 opcode = recv_data.GetOpcode();
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd %s (%u, 0x%X) opcode", LookupOpcodeName(opcode), opcode, opcode);
/* extract packet */
uint64 guid;
uint32 unk1;
float newspeed;
recv_data.readPackGUID(guid);
// now can skip not our packet
if (_player->GetGUID() != guid)
{
recv_data.rfinish(); // prevent warnings spam
return;
}
// continue parse packet
recv_data >> unk1; // counter or moveEvent
MovementInfo movementInfo;
movementInfo.guid = guid;
ReadMovementInfo(recv_data, &movementInfo);
recv_data >> newspeed;
/*----------------*/
// client ACK send one packet for mounted/run case and need skip all except last from its
// in other cases anti-cheat check can be fail in false case
UnitMoveType move_type;
UnitMoveType force_move_type;
static char const* move_type_name[MAX_MOVE_TYPE] = { "Walk", "Run", "RunBack", "Swim", "SwimBack", "TurnRate", "Flight", "FlightBack", "PitchRate" };
switch (opcode)
{
case CMSG_FORCE_WALK_SPEED_CHANGE_ACK: move_type = MOVE_WALK; force_move_type = MOVE_WALK; break;
case CMSG_FORCE_RUN_SPEED_CHANGE_ACK: move_type = MOVE_RUN; force_move_type = MOVE_RUN; break;
case CMSG_FORCE_RUN_BACK_SPEED_CHANGE_ACK: move_type = MOVE_RUN_BACK; force_move_type = MOVE_RUN_BACK; break;
case CMSG_FORCE_SWIM_SPEED_CHANGE_ACK: move_type = MOVE_SWIM; force_move_type = MOVE_SWIM; break;
case CMSG_FORCE_SWIM_BACK_SPEED_CHANGE_ACK: move_type = MOVE_SWIM_BACK; force_move_type = MOVE_SWIM_BACK; break;
case CMSG_FORCE_TURN_RATE_CHANGE_ACK: move_type = MOVE_TURN_RATE; force_move_type = MOVE_TURN_RATE; break;
case CMSG_FORCE_FLIGHT_SPEED_CHANGE_ACK: move_type = MOVE_FLIGHT; force_move_type = MOVE_FLIGHT; break;
case CMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE_ACK: move_type = MOVE_FLIGHT_BACK; force_move_type = MOVE_FLIGHT_BACK; break;
case CMSG_FORCE_PITCH_RATE_CHANGE_ACK: move_type = MOVE_PITCH_RATE; force_move_type = MOVE_PITCH_RATE; break;
default:
sLog->outError("WorldSession::HandleForceSpeedChangeAck: Unknown move type opcode: %u", opcode);
return;
}
// skip all forced speed changes except last and unexpected
// in run/mounted case used one ACK and it must be skipped.m_forced_speed_changes[MOVE_RUN} store both.
if (_player->m_forced_speed_changes[force_move_type] > 0)
{
--_player->m_forced_speed_changes[force_move_type];
if (_player->m_forced_speed_changes[force_move_type] > 0)
return;
}
if (!_player->GetTransport() && fabs(_player->GetSpeed(move_type) - newspeed) > 0.01f)
{
if (_player->GetSpeed(move_type) > newspeed) // must be greater - just correct
{
sLog->outError("%sSpeedChange player %s is NOT correct (must be %f instead %f), force set to correct value",
move_type_name[move_type], _player->GetName(), _player->GetSpeed(move_type), newspeed);
_player->SetSpeed(move_type, _player->GetSpeedRate(move_type), true);
}
else // must be lesser - cheating
{
sLog->outBasic("Player %s from account id %u kicked for incorrect speed (must be %f instead %f)",
_player->GetName(), _player->GetSession()->GetAccountId(), _player->GetSpeed(move_type), newspeed);
_player->GetSession()->KickPlayer();
}
}
}
void WorldSession::HandleSetActiveMoverOpcode(WorldPacket &recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_SET_ACTIVE_MOVER");
uint64 guid;
recv_data >> guid;
if (GetPlayer()->IsInWorld())
{
if (_player->m_mover->GetGUID() != guid)
sLog->outError("HandleSetActiveMoverOpcode: incorrect mover guid: mover is " UI64FMTD " (%s - Entry: %u) and should be " UI64FMTD, guid, GetLogNameForGuid(guid), GUID_ENPART(guid), _player->m_mover->GetGUID());
}
}
void WorldSession::HandleMoveNotActiveMover(WorldPacket &recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_MOVE_NOT_ACTIVE_MOVER");
uint64 old_mover_guid;
recv_data.readPackGUID(old_mover_guid);
MovementInfo mi;
mi.guid = old_mover_guid;
ReadMovementInfo(recv_data, &mi);
_player->m_movementInfo = mi;
}
void WorldSession::HandleMountSpecialAnimOpcode(WorldPacket& /*recv_data*/)
{
WorldPacket data(SMSG_MOUNTSPECIAL_ANIM, 8);
data << uint64(GetPlayer()->GetGUID());
GetPlayer()->SendMessageToSet(&data, false);
}
void WorldSession::HandleMoveKnockBackAck(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_MOVE_KNOCK_BACK_ACK");
uint64 guid;
recv_data.readPackGUID(guid);
if (_player->m_mover->GetGUID() != guid)
return;
recv_data.read_skip<uint32>(); // unk
MovementInfo movementInfo;
ReadMovementInfo(recv_data, &movementInfo);
_player->m_movementInfo = movementInfo;
WorldPacket data(MSG_MOVE_KNOCK_BACK, 66);
data.appendPackGUID(guid);
_player->BuildMovementPacket(&data);
// knockback specific info
data << movementInfo.j_sinAngle;
data << movementInfo.j_cosAngle;
data << movementInfo.j_xyspeed;
data << movementInfo.j_zspeed;
_player->SendMessageToSet(&data, false);
}
void WorldSession::HandleMoveHoverAck(WorldPacket& recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_MOVE_HOVER_ACK");
uint64 guid; // guid - unused
recv_data.readPackGUID(guid);
recv_data.read_skip<uint32>(); // unk
MovementInfo movementInfo;
ReadMovementInfo(recv_data, &movementInfo);
recv_data.read_skip<uint32>(); // unk2
}
void WorldSession::HandleMoveWaterWalkAck(WorldPacket& recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_MOVE_WATER_WALK_ACK");
uint64 guid; // guid - unused
recv_data.readPackGUID(guid);
recv_data.read_skip<uint32>(); // unk
MovementInfo movementInfo;
ReadMovementInfo(recv_data, &movementInfo);
recv_data.read_skip<uint32>(); // unk2
}
void WorldSession::HandleSummonResponseOpcode(WorldPacket& recv_data)
{
if (!_player->isAlive() || _player->isInCombat())
return;
uint64 summoner_guid;
bool agree;
recv_data >> summoner_guid;
recv_data >> agree;
_player->SummonIfPossible(agree);
}
+909
View File
@@ -0,0 +1,909 @@
/*
* Copyright (C) 2008-2012 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 "WorldSession.h"
#include "Opcodes.h"
#include "Log.h"
#include "ObjectMgr.h"
#include "SpellMgr.h"
#include "Player.h"
#include "GossipDef.h"
#include "UpdateMask.h"
#include "ObjectAccessor.h"
#include "Creature.h"
#include "Pet.h"
#include "BattlegroundMgr.h"
#include "Battleground.h"
#include "ScriptMgr.h"
#include "CreatureAI.h"
#include "SpellInfo.h"
enum StableResultCode
{
STABLE_ERR_MONEY = 0x01, // "you don't have enough money"
STABLE_ERR_STABLE = 0x06, // currently used in most fail cases
STABLE_SUCCESS_STABLE = 0x08, // stable success
STABLE_SUCCESS_UNSTABLE = 0x09, // unstable/swap success
STABLE_SUCCESS_BUY_SLOT = 0x0A, // buy slot success
STABLE_ERR_EXOTIC = 0x0C, // "you are unable to control exotic creatures"
};
void WorldSession::HandleTabardVendorActivateOpcode(WorldPacket & recv_data)
{
uint64 guid;
recv_data >> guid;
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_TABARDDESIGNER);
if (!unit)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleTabardVendorActivateOpcode - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(guid)));
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
SendTabardVendorActivate(guid);
}
void WorldSession::SendTabardVendorActivate(uint64 guid)
{
WorldPacket data(MSG_TABARDVENDOR_ACTIVATE, 8);
data << guid;
SendPacket(&data);
}
void WorldSession::HandleBankerActivateOpcode(WorldPacket & recv_data)
{
uint64 guid;
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_BANKER_ACTIVATE");
recv_data >> guid;
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_BANKER);
if (!unit)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleBankerActivateOpcode - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(guid)));
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
SendShowBank(guid);
}
void WorldSession::SendShowBank(uint64 guid)
{
WorldPacket data(SMSG_SHOW_BANK, 8);
data << guid;
SendPacket(&data);
}
void WorldSession::HandleTrainerListOpcode(WorldPacket & recv_data)
{
uint64 guid;
recv_data >> guid;
SendTrainerList(guid);
}
void WorldSession::SendTrainerList(uint64 guid)
{
std::string str = GetTrinityString(LANG_NPC_TAINER_HELLO);
SendTrainerList(guid, str);
}
void WorldSession::SendTrainerList(uint64 guid, const std::string& strTitle)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: SendTrainerList");
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_TRAINER);
if (!unit)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: SendTrainerList - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(guid)));
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
// trainer list loaded at check;
if (!unit->isCanTrainingOf(_player, true))
return;
CreatureTemplate const* ci = unit->GetCreatureInfo();
if (!ci)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: SendTrainerList - (GUID: %u) NO CREATUREINFO!", GUID_LOPART(guid));
return;
}
TrainerSpellData const* trainer_spells = unit->GetTrainerSpells();
if (!trainer_spells)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: SendTrainerList - Training spells not found for creature (GUID: %u Entry: %u)",
GUID_LOPART(guid), unit->GetEntry());
return;
}
WorldPacket data(SMSG_TRAINER_LIST, 8+4+4+trainer_spells->spellList.size()*38 + strTitle.size()+1);
data << guid;
data << uint32(trainer_spells->trainerType);
size_t count_pos = data.wpos();
data << uint32(trainer_spells->spellList.size());
// reputation discount
float fDiscountMod = _player->GetReputationPriceDiscount(unit);
bool can_learn_primary_prof = GetPlayer()->GetFreePrimaryProfessionPoints() > 0;
uint32 count = 0;
for (TrainerSpellMap::const_iterator itr = trainer_spells->spellList.begin(); itr != trainer_spells->spellList.end(); ++itr)
{
TrainerSpell const* tSpell = &itr->second;
bool valid = true;
bool primary_prof_first_rank = false;
for (uint8 i = 0; i < MAX_SPELL_EFFECTS ; ++i)
{
if (!tSpell->learnedSpell[i])
continue;
if (!_player->IsSpellFitByClassAndRace(tSpell->learnedSpell[i]))
{
valid = false;
break;
}
SpellInfo const* learnedSpellInfo = sSpellMgr->GetSpellInfo(tSpell->learnedSpell[i]);
if (learnedSpellInfo && learnedSpellInfo->IsPrimaryProfessionFirstRank())
primary_prof_first_rank = true;
}
if (!valid)
continue;
TrainerSpellState state = _player->GetTrainerSpellState(tSpell);
data << uint32(tSpell->spell); // learned spell (or cast-spell in profession case)
data << uint8(state == TRAINER_SPELL_GREEN_DISABLED ? TRAINER_SPELL_GREEN : state);
data << uint32(floor(tSpell->spellCost * fDiscountMod));
data << uint32(primary_prof_first_rank && can_learn_primary_prof ? 1 : 0);
// primary prof. learn confirmation dialog
data << uint32(primary_prof_first_rank ? 1 : 0); // must be equal prev. field to have learn button in enabled state
data << uint8(tSpell->reqLevel);
data << uint32(tSpell->reqSkill);
data << uint32(tSpell->reqSkillValue);
//prev + req or req + 0
uint8 maxReq = 0;
for (uint8 i = 0; i < MAX_SPELL_EFFECTS ; ++i)
{
if (!tSpell->learnedSpell[i])
continue;
if (uint32 prevSpellId = sSpellMgr->GetPrevSpellInChain(tSpell->learnedSpell[i]))
{
data << uint32(prevSpellId);
++maxReq;
}
if (maxReq == 3)
break;
SpellsRequiringSpellMapBounds spellsRequired = sSpellMgr->GetSpellsRequiredForSpellBounds(tSpell->learnedSpell[i]);
for (SpellsRequiringSpellMap::const_iterator itr2 = spellsRequired.first; itr2 != spellsRequired.second && maxReq < 3; ++itr2)
{
data << uint32(itr2->second);
++maxReq;
}
if (maxReq == 3)
break;
}
while (maxReq < 3)
{
data << uint32(0);
++maxReq;
}
++count;
}
data << strTitle;
data.put<uint32>(count_pos, count);
SendPacket(&data);
}
void WorldSession::HandleTrainerBuySpellOpcode(WorldPacket & recv_data)
{
uint64 guid;
uint32 spellId = 0;
recv_data >> guid >> spellId;
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_TRAINER_BUY_SPELL NpcGUID=%u, learn spell id is: %u", uint32(GUID_LOPART(guid)), spellId);
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_TRAINER);
if (!unit)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleTrainerBuySpellOpcode - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(guid)));
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
if (!unit->isCanTrainingOf(_player, true))
return;
// check present spell in trainer spell list
TrainerSpellData const* trainer_spells = unit->GetTrainerSpells();
if (!trainer_spells)
return;
// not found, cheat?
TrainerSpell const* trainer_spell = trainer_spells->Find(spellId);
if (!trainer_spell)
return;
// can't be learn, cheat? Or double learn with lags...
if (_player->GetTrainerSpellState(trainer_spell) != TRAINER_SPELL_GREEN)
return;
// apply reputation discount
uint32 nSpellCost = uint32(floor(trainer_spell->spellCost * _player->GetReputationPriceDiscount(unit)));
// check money requirement
if (!_player->HasEnoughMoney(nSpellCost))
return;
_player->ModifyMoney(-int32(nSpellCost));
unit->SendPlaySpellVisual(179); // 53 SpellCastDirected
unit->SendPlaySpellImpact(_player->GetGUID(), 362); // 113 EmoteSalute
// learn explicitly or cast explicitly
if (trainer_spell->IsCastable())
_player->CastSpell(_player, trainer_spell->spell, true);
else
_player->learnSpell(spellId, false);
WorldPacket data(SMSG_TRAINER_BUY_SUCCEEDED, 12);
data << uint64(guid);
data << uint32(spellId); // should be same as in packet from client
SendPacket(&data);
}
void WorldSession::HandleGossipHelloOpcode(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GOSSIP_HELLO");
uint64 guid;
recv_data >> guid;
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE);
if (!unit)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleGossipHelloOpcode - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(guid)));
return;
}
// set faction visible if needed
if (FactionTemplateEntry const* factionTemplateEntry = sFactionTemplateStore.LookupEntry(unit->getFaction()))
_player->GetReputationMgr().SetVisible(factionTemplateEntry);
GetPlayer()->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TALK);
// remove fake death
//if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
// GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
if (unit->isArmorer() || unit->isCivilian() || unit->isQuestGiver() || unit->isServiceProvider() || unit->isGuard())
{
unit->StopMoving();
}
// If spiritguide, no need for gossip menu, just put player into resurrect queue
if (unit->isSpiritGuide())
{
Battleground* bg = _player->GetBattleground();
if (bg)
{
bg->AddPlayerToResurrectQueue(unit->GetGUID(), _player->GetGUID());
sBattlegroundMgr->SendAreaSpiritHealerQueryOpcode(_player, bg, unit->GetGUID());
return;
}
}
if (!sScriptMgr->OnGossipHello(_player, unit))
{
// _player->TalkedToCreature(unit->GetEntry(), unit->GetGUID());
_player->PrepareGossipMenu(unit, unit->GetCreatureInfo()->GossipMenuId, true);
_player->SendPreparedGossip(unit);
}
unit->AI()->sGossipHello(_player);
}
/*void WorldSession::HandleGossipSelectOptionOpcode(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_PACKETIO, "WORLD: CMSG_GOSSIP_SELECT_OPTION");
uint32 option;
uint32 unk;
uint64 guid;
std::string code = "";
recv_data >> guid >> unk >> option;
if (_player->PlayerTalkClass->GossipOptionCoded(option))
{
sLog->outDebug(LOG_FILTER_PACKETIO, "reading string");
recv_data >> code;
sLog->outDebug(LOG_FILTER_PACKETIO, "string read: %s", code.c_str());
}
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE);
if (!unit)
{
sLog->outDebug(LOG_FILTER_PACKETIO, "WORLD: HandleGossipSelectOptionOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)));
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
if (!code.empty())
{
if (!Script->GossipSelectWithCode(_player, unit, _player->PlayerTalkClass->GossipOptionSender (option), _player->PlayerTalkClass->GossipOptionAction(option), code.c_str()))
unit->OnGossipSelect (_player, option);
}
else
{
if (!Script->OnGossipSelect (_player, unit, _player->PlayerTalkClass->GossipOptionSender (option), _player->PlayerTalkClass->GossipOptionAction (option)))
unit->OnGossipSelect (_player, option);
}
}*/
void WorldSession::HandleSpiritHealerActivateOpcode(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_SPIRIT_HEALER_ACTIVATE");
uint64 guid;
recv_data >> guid;
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_SPIRITHEALER);
if (!unit)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleSpiritHealerActivateOpcode - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(guid)));
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
SendSpiritResurrect();
}
void WorldSession::SendSpiritResurrect()
{
_player->ResurrectPlayer(0.5f, true);
_player->DurabilityLossAll(0.25f, true);
// get corpse nearest graveyard
WorldSafeLocsEntry const* corpseGrave = NULL;
Corpse* corpse = _player->GetCorpse();
if (corpse)
corpseGrave = sObjectMgr->GetClosestGraveYard(
corpse->GetPositionX(), corpse->GetPositionY(), corpse->GetPositionZ(), corpse->GetMapId(), _player->GetTeam());
// now can spawn bones
_player->SpawnCorpseBones();
// teleport to nearest from corpse graveyard, if different from nearest to player ghost
if (corpseGrave)
{
WorldSafeLocsEntry const* ghostGrave = sObjectMgr->GetClosestGraveYard(
_player->GetPositionX(), _player->GetPositionY(), _player->GetPositionZ(), _player->GetMapId(), _player->GetTeam());
if (corpseGrave != ghostGrave)
_player->TeleportTo(corpseGrave->map_id, corpseGrave->x, corpseGrave->y, corpseGrave->z, _player->GetOrientation());
// or update at original position
else
_player->UpdateObjectVisibility();
}
// or update at original position
else
_player->UpdateObjectVisibility();
}
void WorldSession::HandleBinderActivateOpcode(WorldPacket & recv_data)
{
uint64 npcGUID;
recv_data >> npcGUID;
if (!GetPlayer()->IsInWorld() || !GetPlayer()->isAlive())
return;
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(npcGUID, UNIT_NPC_FLAG_INNKEEPER);
if (!unit)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleBinderActivateOpcode - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(npcGUID)));
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
SendBindPoint(unit);
}
void WorldSession::SendBindPoint(Creature* npc)
{
// prevent set homebind to instances in any case
if (GetPlayer()->GetMap()->Instanceable())
return;
uint32 bindspell = 3286;
// update sql homebind
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_PLAYER_HOMEBIND);
stmt->setUInt16(0, _player->GetMapId());
stmt->setUInt16(1, _player->GetAreaId());
stmt->setFloat (2, _player->GetPositionX());
stmt->setFloat (3, _player->GetPositionY());
stmt->setFloat (4, _player->GetPositionZ());
stmt->setUInt32(5, _player->GetGUIDLow());
CharacterDatabase.Execute(stmt);
_player->m_homebindMapId = _player->GetMapId();
_player->m_homebindAreaId = _player->GetAreaId();
_player->m_homebindX = _player->GetPositionX();
_player->m_homebindY = _player->GetPositionY();
_player->m_homebindZ = _player->GetPositionZ();
// send spell for homebinding (3286)
npc->CastSpell(_player, bindspell, true);
WorldPacket data(SMSG_TRAINER_BUY_SUCCEEDED, (8+4));
data << uint64(npc->GetGUID());
data << uint32(bindspell);
SendPacket(&data);
_player->PlayerTalkClass->SendCloseGossip();
}
void WorldSession::HandleListStabledPetsOpcode(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recv MSG_LIST_STABLED_PETS");
uint64 npcGUID;
recv_data >> npcGUID;
if (!CheckStableMaster(npcGUID))
return;
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
// remove mounts this fix bug where getting pet from stable while mounted deletes pet.
if (GetPlayer()->IsMounted())
GetPlayer()->RemoveAurasByType(SPELL_AURA_MOUNTED);
SendStablePet(npcGUID);
}
void WorldSession::SendStablePet(uint64 guid)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOTS_DETAIL);
stmt->setUInt32(0, _player->GetGUIDLow());
stmt->setUInt8(1, PET_SAVE_FIRST_STABLE_SLOT);
stmt->setUInt8(2, PET_SAVE_LAST_STABLE_SLOT);
_sendStabledPetCallback.SetParam(guid);
_sendStabledPetCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt));
}
void WorldSession::SendStablePetCallback(PreparedQueryResult result, uint64 guid)
{
if (!GetPlayer())
return;
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recv MSG_LIST_STABLED_PETS Send.");
WorldPacket data(MSG_LIST_STABLED_PETS, 200); // guess size
data << uint64 (guid);
Pet* pet = _player->GetPet();
size_t wpos = data.wpos();
data << uint8(0); // place holder for slot show number
data << uint8(GetPlayer()->m_stableSlots);
uint8 num = 0; // counter for place holder
// not let move dead pet in slot
if (pet && pet->isAlive() && pet->getPetType() == HUNTER_PET)
{
data << uint32(pet->GetCharmInfo()->GetPetNumber());
data << uint32(pet->GetEntry());
data << uint32(pet->getLevel());
data << pet->GetName(); // petname
data << uint8(1); // 1 = current, 2/3 = in stable (any from 4, 5, ... create problems with proper show)
++num;
}
if (result)
{
do
{
Field* fields = result->Fetch();
data << uint32(fields[1].GetUInt32()); // petnumber
data << uint32(fields[2].GetUInt32()); // creature entry
data << uint32(fields[3].GetUInt16()); // level
data << fields[4].GetString(); // name
data << uint8(2); // 1 = current, 2/3 = in stable (any from 4, 5, ... create problems with proper show)
++num;
}
while (result->NextRow());
}
data.put<uint8>(wpos, num); // set real data to placeholder
SendPacket(&data);
}
void WorldSession::SendStableResult(uint8 res)
{
WorldPacket data(SMSG_STABLE_RESULT, 1);
data << uint8(res);
SendPacket(&data);
}
void WorldSession::HandleStablePet(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recv CMSG_STABLE_PET");
uint64 npcGUID;
recv_data >> npcGUID;
if (!GetPlayer()->isAlive())
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
if (!CheckStableMaster(npcGUID))
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
Pet* pet = _player->GetPet();
// can't place in stable dead pet
if (!pet||!pet->isAlive()||pet->getPetType() != HUNTER_PET)
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOTS);
stmt->setUInt32(0, _player->GetGUIDLow());
stmt->setUInt8(1, PET_SAVE_FIRST_STABLE_SLOT);
stmt->setUInt8(2, PET_SAVE_LAST_STABLE_SLOT);
_stablePetCallback = CharacterDatabase.AsyncQuery(stmt);
}
void WorldSession::HandleStablePetCallback(PreparedQueryResult result)
{
if (!GetPlayer())
return;
uint8 freeSlot = 1;
if (result)
{
do
{
Field* fields = result->Fetch();
uint8 slot = fields[1].GetUInt8();
// slots ordered in query, and if not equal then free
if (slot != freeSlot)
break;
// this slot not free, skip
++freeSlot;
}
while (result->NextRow());
}
WorldPacket data(SMSG_STABLE_RESULT, 1);
if (freeSlot > 0 && freeSlot <= GetPlayer()->m_stableSlots)
{
_player->RemovePet(_player->GetPet(), PetSaveMode(freeSlot));
SendStableResult(STABLE_SUCCESS_STABLE);
}
else
SendStableResult(STABLE_ERR_STABLE);
}
void WorldSession::HandleUnstablePet(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recv CMSG_UNSTABLE_PET.");
uint64 npcGUID;
uint32 petnumber;
recv_data >> npcGUID >> petnumber;
if (!CheckStableMaster(npcGUID))
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_ENTRY);
stmt->setUInt32(0, _player->GetGUIDLow());
stmt->setUInt32(1, petnumber);
stmt->setUInt8(2, PET_SAVE_FIRST_STABLE_SLOT);
stmt->setUInt8(3, PET_SAVE_LAST_STABLE_SLOT);
_unstablePetCallback.SetParam(petnumber);
_unstablePetCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt));
}
void WorldSession::HandleUnstablePetCallback(PreparedQueryResult result, uint32 petId)
{
if (!GetPlayer())
return;
uint32 petEntry = 0;
if (result)
{
Field* fields = result->Fetch();
petEntry = fields[0].GetUInt32();
}
if (!petEntry)
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(petEntry);
if (!creatureInfo || !creatureInfo->isTameable(_player->CanTameExoticPets()))
{
// if problem in exotic pet
if (creatureInfo && creatureInfo->isTameable(true))
SendStableResult(STABLE_ERR_EXOTIC);
else
SendStableResult(STABLE_ERR_STABLE);
return;
}
Pet* pet = _player->GetPet();
if (pet && pet->isAlive())
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
// delete dead pet
if (pet)
_player->RemovePet(pet, PET_SAVE_AS_DELETED);
Pet* newPet = new Pet(_player, HUNTER_PET);
if (!newPet->LoadPetFromDB(_player, petEntry, petId))
{
delete newPet;
newPet = NULL;
SendStableResult(STABLE_ERR_STABLE);
return;
}
SendStableResult(STABLE_SUCCESS_UNSTABLE);
}
void WorldSession::HandleBuyStableSlot(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recv CMSG_BUY_STABLE_SLOT.");
uint64 npcGUID;
recv_data >> npcGUID;
if (!CheckStableMaster(npcGUID))
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
if (GetPlayer()->m_stableSlots < MAX_PET_STABLES)
{
StableSlotPricesEntry const* SlotPrice = sStableSlotPricesStore.LookupEntry(GetPlayer()->m_stableSlots+1);
if (_player->HasEnoughMoney(SlotPrice->Price))
{
++GetPlayer()->m_stableSlots;
_player->ModifyMoney(-int32(SlotPrice->Price));
SendStableResult(STABLE_SUCCESS_BUY_SLOT);
}
else
SendStableResult(STABLE_ERR_MONEY);
}
else
SendStableResult(STABLE_ERR_STABLE);
}
void WorldSession::HandleStableRevivePet(WorldPacket &/* recv_data */)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "HandleStableRevivePet: Not implemented");
}
void WorldSession::HandleStableSwapPet(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recv CMSG_STABLE_SWAP_PET.");
uint64 npcGUID;
uint32 petId;
recv_data >> npcGUID >> petId;
if (!CheckStableMaster(npcGUID))
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
Pet* pet = _player->GetPet();
if (!pet || pet->getPetType() != HUNTER_PET)
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
// Find swapped pet slot in stable
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOT_BY_ID);
stmt->setUInt32(0, _player->GetGUIDLow());
stmt->setUInt32(1, petId);
_stableSwapCallback.SetParam(petId);
_stableSwapCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt));
}
void WorldSession::HandleStableSwapPetCallback(PreparedQueryResult result, uint32 petId)
{
if (!GetPlayer())
return;
if (!result)
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
Field* fields = result->Fetch();
uint32 slot = fields[0].GetUInt8();
uint32 petEntry = fields[1].GetUInt32();
if (!petEntry)
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(petEntry);
if (!creatureInfo || !creatureInfo->isTameable(_player->CanTameExoticPets()))
{
// if problem in exotic pet
if (creatureInfo && creatureInfo->isTameable(true))
SendStableResult(STABLE_ERR_EXOTIC);
else
SendStableResult(STABLE_ERR_STABLE);
return;
}
// move alive pet to slot or delete dead pet
Pet* pet = _player->GetPet();
_player->RemovePet(pet, pet->isAlive() ? PetSaveMode(slot) : PET_SAVE_AS_DELETED);
// summon unstabled pet
Pet* newpet = new Pet(_player);
if (!newpet->LoadPetFromDB(_player, petEntry, petId))
{
delete newpet;
SendStableResult(STABLE_ERR_STABLE);
}
else
SendStableResult(STABLE_SUCCESS_UNSTABLE);
}
void WorldSession::HandleRepairItemOpcode(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_REPAIR_ITEM");
uint64 npcGUID, itemGUID;
uint8 guildBank; // new in 2.3.2, bool that means from guild bank money
recv_data >> npcGUID >> itemGUID >> guildBank;
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(npcGUID, UNIT_NPC_FLAG_REPAIR);
if (!unit)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleRepairItemOpcode - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(npcGUID)));
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
// reputation discount
float discountMod = _player->GetReputationPriceDiscount(unit);
if (itemGUID)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "ITEM: Repair item, itemGUID = %u, npcGUID = %u", GUID_LOPART(itemGUID), GUID_LOPART(npcGUID));
Item* item = _player->GetItemByGuid(itemGUID);
if (item)
_player->DurabilityRepair(item->GetPos(), true, discountMod, guildBank);
}
else
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "ITEM: Repair all items, npcGUID = %u", GUID_LOPART(npcGUID));
_player->DurabilityRepairAll(true, discountMod, guildBank);
}
}
+59
View File
@@ -0,0 +1,59 @@
/*
* Copyright (C) 2008-2012 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/>.
*/
#ifndef __NPCHANDLER_H
#define __NPCHANDLER_H
struct QEmote
{
uint32 _Emote;
uint32 _Delay;
};
#define MAX_GOSSIP_TEXT_EMOTES 3
struct GossipTextOption
{
std::string Text_0;
std::string Text_1;
uint32 Language;
float Probability;
QEmote Emotes[MAX_GOSSIP_TEXT_EMOTES];
};
#define MAX_GOSSIP_TEXT_OPTIONS 8
struct GossipText
{
GossipTextOption Options[MAX_GOSSIP_TEXT_OPTIONS];
};
struct PageTextLocale
{
StringVector Text;
};
struct NpcTextLocale
{
NpcTextLocale() { Text_0.resize(8); Text_1.resize(8); }
std::vector<StringVector> Text_0;
std::vector<StringVector> Text_1;
};
#endif
+879
View File
@@ -0,0 +1,879 @@
/*
* Copyright (C) 2008-2012 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 "WorldPacket.h"
#include "WorldSession.h"
#include "ObjectMgr.h"
#include "SpellMgr.h"
#include "Log.h"
#include "Opcodes.h"
#include "Spell.h"
#include "ObjectAccessor.h"
#include "CreatureAI.h"
#include "Util.h"
#include "Pet.h"
#include "World.h"
#include "Group.h"
#include "SpellInfo.h"
void WorldSession::HandleDismissCritter(WorldPacket &recv_data)
{
uint64 guid;
recv_data >> guid;
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_DISMISS_CRITTER for GUID " UI64FMTD, guid);
Unit* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid);
if (!pet)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "Vanitypet (guid: %u) does not exist - player '%s' (guid: %u / account: %u) attempted to dismiss it (possibly lagged out)",
uint32(GUID_LOPART(guid)), GetPlayer()->GetName(), GetPlayer()->GetGUIDLow(), GetAccountId());
return;
}
if (_player->GetCritterGUID() == pet->GetGUID())
{
if (pet->GetTypeId() == TYPEID_UNIT && pet->ToCreature()->isSummon())
pet->ToTempSummon()->UnSummon();
}
}
void WorldSession::HandlePetAction(WorldPacket & recv_data)
{
uint64 guid1;
uint32 data;
uint64 guid2;
recv_data >> guid1; //pet guid
recv_data >> data;
recv_data >> guid2; //tag guid
uint32 spellid = UNIT_ACTION_BUTTON_ACTION(data);
uint8 flag = UNIT_ACTION_BUTTON_TYPE(data); //delete = 0x07 CastSpell = C1
// used also for charmed creature
Unit* pet= ObjectAccessor::GetUnit(*_player, guid1);
sLog->outDetail("HandlePetAction: Pet %u - flag: %u, spellid: %u, target: %u.", uint32(GUID_LOPART(guid1)), uint32(flag), spellid, uint32(GUID_LOPART(guid2)));
if (!pet)
{
sLog->outError("HandlePetAction: Pet (GUID: %u) doesn't exist for player '%s'", uint32(GUID_LOPART(guid1)), GetPlayer()->GetName());
return;
}
if (pet != GetPlayer()->GetFirstControlled())
{
sLog->outError("HandlePetAction: Pet (GUID: %u) does not belong to player '%s'", uint32(GUID_LOPART(guid1)), GetPlayer()->GetName());
return;
}
if (!pet->isAlive())
{
SpellInfo const* spell = (flag == ACT_ENABLED || flag == ACT_PASSIVE) ? sSpellMgr->GetSpellInfo(spellid) : NULL;
if (!spell)
return;
if (!(spell->Attributes & SPELL_ATTR0_CASTABLE_WHILE_DEAD))
return;
}
//TODO: allow control charmed player?
if (pet->GetTypeId() == TYPEID_PLAYER && !(flag == ACT_COMMAND && spellid == COMMAND_ATTACK))
return;
if (GetPlayer()->m_Controlled.size() == 1)
HandlePetActionHelper(pet, guid1, spellid, flag, guid2);
else
{
//If a pet is dismissed, m_Controlled will change
std::vector<Unit*> controlled;
for (Unit::ControlList::iterator itr = GetPlayer()->m_Controlled.begin(); itr != GetPlayer()->m_Controlled.end(); ++itr)
if ((*itr)->GetEntry() == pet->GetEntry() && (*itr)->isAlive())
controlled.push_back(*itr);
for (std::vector<Unit*>::iterator itr = controlled.begin(); itr != controlled.end(); ++itr)
HandlePetActionHelper(*itr, guid1, spellid, flag, guid2);
}
}
void WorldSession::HandlePetStopAttack(WorldPacket &recv_data)
{
uint64 guid;
recv_data >> guid;
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_PET_STOP_ATTACK for GUID " UI64FMTD "", guid);
Unit* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid);
if (!pet)
{
sLog->outError("HandlePetStopAttack: Pet %u does not exist", uint32(GUID_LOPART(guid)));
return;
}
if (pet != GetPlayer()->GetPet() && pet != GetPlayer()->GetCharm())
{
sLog->outError("HandlePetStopAttack: Pet GUID %u isn't a pet or charmed creature of player %s", uint32(GUID_LOPART(guid)), GetPlayer()->GetName());
return;
}
if (!pet->isAlive())
return;
pet->AttackStop();
}
void WorldSession::HandlePetActionHelper(Unit* pet, uint64 guid1, uint16 spellid, uint16 flag, uint64 guid2)
{
CharmInfo* charmInfo = pet->GetCharmInfo();
if (!charmInfo)
{
sLog->outError("WorldSession::HandlePetAction(petGuid: " UI64FMTD ", tagGuid: " UI64FMTD ", spellId: %u, flag: %u): object (entry: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!",
guid1, guid2, spellid, flag, pet->GetGUIDLow(), pet->GetTypeId());
return;
}
switch (flag)
{
case ACT_COMMAND: //0x07
switch (spellid)
{
case COMMAND_STAY: //flat=1792 //STAY
pet->AttackStop();
pet->InterruptNonMeleeSpells(false);
pet->GetMotionMaster()->Clear(false);
pet->GetMotionMaster()->MoveIdle();
charmInfo->SetCommandState(COMMAND_STAY);
charmInfo->SetIsCommandAttack(false);
charmInfo->SetIsAtStay(true);
charmInfo->SetIsFollowing(false);
charmInfo->SetIsReturning(false);
charmInfo->SaveStayPosition();
break;
case COMMAND_FOLLOW: //spellid=1792 //FOLLOW
pet->AttackStop();
pet->InterruptNonMeleeSpells(false);
pet->GetMotionMaster()->MoveFollow(_player, PET_FOLLOW_DIST, pet->GetFollowAngle());
charmInfo->SetCommandState(COMMAND_FOLLOW);
charmInfo->SetIsCommandAttack(false);
charmInfo->SetIsAtStay(false);
charmInfo->SetIsReturning(true);
charmInfo->SetIsFollowing(false);
break;
case COMMAND_ATTACK: //spellid=1792 //ATTACK
{
// Can't attack if owner is pacified
if (_player->HasAuraType(SPELL_AURA_MOD_PACIFY))
{
//pet->SendPetCastFail(spellid, SPELL_FAILED_PACIFIED);
//TODO: Send proper error message to client
return;
}
// only place where pet can be player
Unit* TargetUnit = ObjectAccessor::GetUnit(*_player, guid2);
if (!TargetUnit)
return;
if (Unit* owner = pet->GetOwner())
if (!owner->IsValidAttackTarget(TargetUnit))
return;
// Not let attack through obstructions
if (sWorld->getBoolConfig(CONFIG_PET_LOS))
{
if (!pet->IsWithinLOSInMap(TargetUnit))
return;
}
pet->ClearUnitState(UNIT_STAT_FOLLOW);
// This is true if pet has no target or has target but targets differs.
if (pet->getVictim() != TargetUnit || (pet->getVictim() == TargetUnit && !pet->GetCharmInfo()->IsCommandAttack()))
{
if (pet->getVictim())
pet->AttackStop();
if (pet->GetTypeId() != TYPEID_PLAYER && pet->ToCreature()->IsAIEnabled)
{
charmInfo->SetIsCommandAttack(true);
charmInfo->SetIsAtStay(false);
charmInfo->SetIsFollowing(false);
charmInfo->SetIsReturning(false);
pet->ToCreature()->AI()->AttackStart(TargetUnit);
//10% chance to play special pet attack talk, else growl
if (pet->ToCreature()->isPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != TargetUnit && urand(0, 100) < 10)
pet->SendPetTalk((uint32)PET_TALK_ATTACK);
else
{
// 90% chance for pet and 100% chance for charmed creature
pet->SendPetAIReaction(guid1);
}
}
else // charmed player
{
if (pet->getVictim() && pet->getVictim() != TargetUnit)
pet->AttackStop();
charmInfo->SetIsCommandAttack(true);
charmInfo->SetIsAtStay(false);
charmInfo->SetIsFollowing(false);
charmInfo->SetIsReturning(false);
pet->Attack(TargetUnit, true);
pet->SendPetAIReaction(guid1);
}
}
break;
}
case COMMAND_ABANDON: // abandon (hunter pet) or dismiss (summoned pet)
if (pet->GetCharmerGUID() == GetPlayer()->GetGUID())
_player->StopCastingCharm();
else if (pet->GetOwnerGUID() == GetPlayer()->GetGUID())
{
ASSERT(pet->GetTypeId() == TYPEID_UNIT);
if (pet->isPet())
{
if (((Pet*)pet)->getPetType() == HUNTER_PET)
GetPlayer()->RemovePet((Pet*)pet, PET_SAVE_AS_DELETED);
else
//dismissing a summoned pet is like killing them (this prevents returning a soulshard...)
pet->setDeathState(CORPSE);
}
else if (pet->HasUnitTypeMask(UNIT_MASK_MINION))
{
((Minion*)pet)->UnSummon();
}
}
break;
default:
sLog->outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid);
}
break;
case ACT_REACTION: // 0x6
switch (spellid)
{
case REACT_PASSIVE: //passive
pet->AttackStop();
case REACT_DEFENSIVE: //recovery
case REACT_AGGRESSIVE: //activete
if (pet->GetTypeId() == TYPEID_UNIT)
pet->ToCreature()->SetReactState(ReactStates(spellid));
break;
}
break;
case ACT_DISABLED: // 0x81 spell (disabled), ignore
case ACT_PASSIVE: // 0x01
case ACT_ENABLED: // 0xC1 spell
{
Unit* unit_target = NULL;
if (guid2)
unit_target = ObjectAccessor::GetUnit(*_player, guid2);
// do not cast unknown spells
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellid);
if (!spellInfo)
{
sLog->outError("WORLD: unknown PET spell id %i", spellid);
return;
}
if (spellInfo->StartRecoveryCategory > 0)
if (pet->GetCharmInfo() && pet->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo))
return;
for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
if (spellInfo->Effects[i].TargetA.GetTarget() == TARGET_UNIT_SRC_AREA_ENEMY || spellInfo->Effects[i].TargetA.GetTarget() == TARGET_UNIT_DEST_AREA_ENEMY || spellInfo->Effects[i].TargetA.GetTarget() == TARGET_DEST_DYNOBJ_ENEMY)
return;
}
// do not cast not learned spells
if (!pet->HasSpell(spellid) || spellInfo->IsPassive())
return;
// Clear the flags as if owner clicked 'attack'. AI will reset them
// after AttackStart, even if spell failed
if (pet->GetCharmInfo())
{
pet->GetCharmInfo()->SetIsAtStay(false);
pet->GetCharmInfo()->SetIsCommandAttack(true);
pet->GetCharmInfo()->SetIsReturning(false);
pet->GetCharmInfo()->SetIsFollowing(false);
}
Spell* spell = new Spell(pet, spellInfo, TRIGGERED_NONE);
SpellCastResult result = spell->CheckPetCast(unit_target);
//auto turn to target unless possessed
if (result == SPELL_FAILED_UNIT_NOT_INFRONT && !pet->isPossessed() && !pet->IsVehicle())
{
if (unit_target)
{
pet->SetInFront(unit_target);
if (unit_target->GetTypeId() == TYPEID_PLAYER)
pet->SendUpdateToPlayer((Player*)unit_target);
}
else if (Unit* unit_target2 = spell->m_targets.GetUnitTarget())
{
pet->SetInFront(unit_target2);
if (unit_target2->GetTypeId() == TYPEID_PLAYER)
pet->SendUpdateToPlayer((Player*)unit_target2);
}
if (Unit* powner = pet->GetCharmerOrOwner())
if (powner->GetTypeId() == TYPEID_PLAYER)
pet->SendUpdateToPlayer(powner->ToPlayer());
result = SPELL_CAST_OK;
}
if (result == SPELL_CAST_OK)
{
pet->ToCreature()->AddCreatureSpellCooldown(spellid);
unit_target = spell->m_targets.GetUnitTarget();
//10% chance to play special pet attack talk, else growl
//actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell
if (pet->ToCreature()->isPet() && (((Pet*)pet)->getPetType() == SUMMON_PET) && (pet != unit_target) && (urand(0, 100) < 10))
pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL);
else
{
pet->SendPetAIReaction(guid1);
}
if (unit_target && !GetPlayer()->IsFriendlyTo(unit_target) && !pet->isPossessed() && !pet->IsVehicle())
{
// This is true if pet has no target or has target but targets differs.
if (pet->getVictim() != unit_target)
{
if (pet->getVictim())
pet->AttackStop();
pet->GetMotionMaster()->Clear();
if (pet->ToCreature()->IsAIEnabled)
pet->ToCreature()->AI()->AttackStart(unit_target);
}
}
spell->prepare(&(spell->m_targets));
}
else
{
if (pet->isPossessed() || pet->IsVehicle())
Spell::SendCastResult(GetPlayer(), spellInfo, 0, result);
else
pet->SendPetCastFail(spellid, result);
if (!pet->ToCreature()->HasSpellCooldown(spellid))
GetPlayer()->SendClearCooldown(spellid, pet);
spell->finish(false);
delete spell;
// reset specific flags in case of spell fail. AI will reset other flags
if (pet->GetCharmInfo())
pet->GetCharmInfo()->SetIsCommandAttack(false);
}
break;
}
default:
sLog->outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid);
}
}
void WorldSession::HandlePetNameQuery(WorldPacket & recv_data)
{
sLog->outDetail("HandlePetNameQuery. CMSG_PET_NAME_QUERY");
uint32 petnumber;
uint64 petguid;
recv_data >> petnumber;
recv_data >> petguid;
SendPetNameQuery(petguid, petnumber);
}
void WorldSession::SendPetNameQuery(uint64 petguid, uint32 petnumber)
{
Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, petguid);
if (!pet)
{
WorldPacket data(SMSG_PET_NAME_QUERY_RESPONSE, (4+1+4+1));
data << uint32(petnumber);
data << uint8(0);
data << uint32(0);
data << uint8(0);
_player->GetSession()->SendPacket(&data);
return;
}
std::string name = pet->GetName();
WorldPacket data(SMSG_PET_NAME_QUERY_RESPONSE, (4+4+name.size()+1));
data << uint32(petnumber);
data << name.c_str();
data << uint32(pet->GetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP));
if (pet->isPet() && ((Pet*)pet)->GetDeclinedNames())
{
data << uint8(1);
for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
data << ((Pet*)pet)->GetDeclinedNames()->name[i];
}
else
data << uint8(0);
_player->GetSession()->SendPacket(&data);
}
bool WorldSession::CheckStableMaster(uint64 guid)
{
// spell case or GM
if (guid == GetPlayer()->GetGUID())
{
if (!GetPlayer()->isGameMaster() && !GetPlayer()->HasAuraType(SPELL_AURA_OPEN_STABLE))
{
sLog->outStaticDebug("Player (GUID:%u) attempt open stable in cheating way.", GUID_LOPART(guid));
return false;
}
}
// stable master case
else
{
if (!GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_STABLEMASTER))
{
sLog->outStaticDebug("Stablemaster (GUID:%u) not found or you can't interact with him.", GUID_LOPART(guid));
return false;
}
}
return true;
}
void WorldSession::HandlePetSetAction(WorldPacket & recv_data)
{
sLog->outDetail("HandlePetSetAction. CMSG_PET_SET_ACTION");
uint64 petguid;
uint8 count;
recv_data >> petguid;
Unit* pet = ObjectAccessor::GetUnit(*_player, petguid);
if (!pet || pet != _player->GetFirstControlled())
{
sLog->outError("HandlePetSetAction: Unknown pet (GUID: %u) or pet owner (GUID: %u)", GUID_LOPART(petguid), _player->GetGUIDLow());
return;
}
CharmInfo* charmInfo = pet->GetCharmInfo();
if (!charmInfo)
{
sLog->outError("WorldSession::HandlePetSetAction: object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", pet->GetGUIDLow(), pet->GetTypeId());
return;
}
count = (recv_data.size() == 24) ? 2 : 1;
uint32 position[2];
uint32 data[2];
bool move_command = false;
for (uint8 i = 0; i < count; ++i)
{
recv_data >> position[i];
recv_data >> data[i];
uint8 act_state = UNIT_ACTION_BUTTON_TYPE(data[i]);
//ignore invalid position
if (position[i] >= MAX_UNIT_ACTION_BAR_INDEX)
return;
// in the normal case, command and reaction buttons can only be moved, not removed
// at moving count == 2, at removing count == 1
// ignore attempt to remove command|reaction buttons (not possible at normal case)
if (act_state == ACT_COMMAND || act_state == ACT_REACTION)
{
if (count == 1)
return;
move_command = true;
}
}
// check swap (at command->spell swap client remove spell first in another packet, so check only command move correctness)
if (move_command)
{
uint8 act_state_0 = UNIT_ACTION_BUTTON_TYPE(data[0]);
if (act_state_0 == ACT_COMMAND || act_state_0 == ACT_REACTION)
{
uint32 spell_id_0 = UNIT_ACTION_BUTTON_ACTION(data[0]);
UnitActionBarEntry const* actionEntry_1 = charmInfo->GetActionBarEntry(position[1]);
if (!actionEntry_1 || spell_id_0 != actionEntry_1->GetAction() ||
act_state_0 != actionEntry_1->GetType())
return;
}
uint8 act_state_1 = UNIT_ACTION_BUTTON_TYPE(data[1]);
if (act_state_1 == ACT_COMMAND || act_state_1 == ACT_REACTION)
{
uint32 spell_id_1 = UNIT_ACTION_BUTTON_ACTION(data[1]);
UnitActionBarEntry const* actionEntry_0 = charmInfo->GetActionBarEntry(position[0]);
if (!actionEntry_0 || spell_id_1 != actionEntry_0->GetAction() ||
act_state_1 != actionEntry_0->GetType())
return;
}
}
for (uint8 i = 0; i < count; ++i)
{
uint32 spell_id = UNIT_ACTION_BUTTON_ACTION(data[i]);
uint8 act_state = UNIT_ACTION_BUTTON_TYPE(data[i]);
sLog->outDetail("Player %s has changed pet spell action. Position: %u, Spell: %u, State: 0x%X", _player->GetName(), position[i], spell_id, uint32(act_state));
//if it's act for spell (en/disable/cast) and there is a spell given (0 = remove spell) which pet doesn't know, don't add
if (!((act_state == ACT_ENABLED || act_state == ACT_DISABLED || act_state == ACT_PASSIVE) && spell_id && !pet->HasSpell(spell_id)))
{
if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell_id))
{
//sign for autocast
if (act_state == ACT_ENABLED)
{
if (pet->GetTypeId() == TYPEID_UNIT && pet->ToCreature()->isPet())
((Pet*)pet)->ToggleAutocast(spellInfo, true);
else
for (Unit::ControlList::iterator itr = GetPlayer()->m_Controlled.begin(); itr != GetPlayer()->m_Controlled.end(); ++itr)
if ((*itr)->GetEntry() == pet->GetEntry())
(*itr)->GetCharmInfo()->ToggleCreatureAutocast(spellInfo, true);
}
//sign for no/turn off autocast
else if (act_state == ACT_DISABLED)
{
if (pet->GetTypeId() == TYPEID_UNIT && pet->ToCreature()->isPet())
((Pet*)pet)->ToggleAutocast(spellInfo, false);
else
for (Unit::ControlList::iterator itr = GetPlayer()->m_Controlled.begin(); itr != GetPlayer()->m_Controlled.end(); ++itr)
if ((*itr)->GetEntry() == pet->GetEntry())
(*itr)->GetCharmInfo()->ToggleCreatureAutocast(spellInfo, false);
}
}
charmInfo->SetActionBar(position[i], spell_id, ActiveStates(act_state));
}
}
}
void WorldSession::HandlePetRename(WorldPacket & recv_data)
{
sLog->outDetail("HandlePetRename. CMSG_PET_RENAME");
uint64 petguid;
uint8 isdeclined;
std::string name;
DeclinedName declinedname;
recv_data >> petguid;
recv_data >> name;
recv_data >> isdeclined;
Pet* pet = ObjectAccessor::FindPet(petguid);
// check it!
if (!pet || !pet->isPet() || ((Pet*)pet)->getPetType()!= HUNTER_PET ||
!pet->HasByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED) ||
pet->GetOwnerGUID() != _player->GetGUID() || !pet->GetCharmInfo())
return;
PetNameInvalidReason res = ObjectMgr::CheckPetName(name);
if (res != PET_NAME_SUCCESS)
{
SendPetNameInvalid(res, name, NULL);
return;
}
if (sObjectMgr->IsReservedName(name))
{
SendPetNameInvalid(PET_NAME_RESERVED, name, NULL);
return;
}
pet->SetName(name);
Unit* owner = pet->GetOwner();
if (owner && (owner->GetTypeId() == TYPEID_PLAYER) && owner->ToPlayer()->GetGroup())
owner->ToPlayer()->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_NAME);
pet->RemoveByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED);
if (isdeclined)
{
for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
{
recv_data >> declinedname.name[i];
}
std::wstring wname;
Utf8toWStr(name, wname);
if (!ObjectMgr::CheckDeclinedNames(wname, declinedname))
{
SendPetNameInvalid(PET_NAME_DECLENSION_DOESNT_MATCH_BASE_NAME, name, &declinedname);
return;
}
}
SQLTransaction trans = CharacterDatabase.BeginTransaction();
if (isdeclined)
{
for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
CharacterDatabase.EscapeString(declinedname.name[i]);
trans->PAppend("DELETE FROM character_pet_declinedname WHERE owner = '%u' AND id = '%u'", _player->GetGUIDLow(), pet->GetCharmInfo()->GetPetNumber());
trans->PAppend("INSERT INTO character_pet_declinedname (id, owner, genitive, dative, accusative, instrumental, prepositional) VALUES ('%u', '%u', '%s', '%s', '%s', '%s', '%s')",
pet->GetCharmInfo()->GetPetNumber(), _player->GetGUIDLow(), declinedname.name[0].c_str(), declinedname.name[1].c_str(), declinedname.name[2].c_str(), declinedname.name[3].c_str(), declinedname.name[4].c_str());
}
CharacterDatabase.EscapeString(name);
trans->PAppend("UPDATE character_pet SET name = '%s', renamed = '1' WHERE owner = '%u' AND id = '%u'", name.c_str(), _player->GetGUIDLow(), pet->GetCharmInfo()->GetPetNumber());
CharacterDatabase.CommitTransaction(trans);
pet->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, uint32(time(NULL))); // cast can't be helped
}
void WorldSession::HandlePetAbandon(WorldPacket & recv_data)
{
uint64 guid;
recv_data >> guid; //pet guid
sLog->outDetail("HandlePetAbandon. CMSG_PET_ABANDON pet guid is %u", GUID_LOPART(guid));
if (!_player->IsInWorld())
return;
// pet/charmed
Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid);
if (pet)
{
if (pet->isPet())
{
if (pet->GetGUID() == _player->GetPetGUID())
{
uint32 feelty = pet->GetPower(POWER_HAPPINESS);
pet->SetPower(POWER_HAPPINESS, feelty > 50000 ? (feelty-50000) : 0);
}
_player->RemovePet((Pet*)pet, PET_SAVE_AS_DELETED);
}
else if (pet->GetGUID() == _player->GetCharmGUID())
_player->StopCastingCharm();
}
}
void WorldSession::HandlePetSpellAutocastOpcode(WorldPacket& recvPacket)
{
sLog->outDetail("CMSG_PET_SPELL_AUTOCAST");
uint64 guid;
uint32 spellid;
uint8 state; //1 for on, 0 for off
recvPacket >> guid >> spellid >> state;
if (!_player->GetGuardianPet() && !_player->GetCharm())
return;
if (ObjectAccessor::FindPlayer(guid))
return;
Creature* pet=ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid);
if (!pet || (pet != _player->GetGuardianPet() && pet != _player->GetCharm()))
{
sLog->outError("HandlePetSpellAutocastOpcode.Pet %u isn't pet of player %s .", uint32(GUID_LOPART(guid)), GetPlayer()->GetName());
return;
}
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellid);
// do not add not learned spells/ passive spells
if (!pet->HasSpell(spellid) || spellInfo->IsAutocastable())
return;
CharmInfo* charmInfo = pet->GetCharmInfo();
if (!charmInfo)
{
sLog->outError("WorldSession::HandlePetSpellAutocastOpcod: object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", pet->GetGUIDLow(), pet->GetTypeId());
return;
}
if (pet->isPet())
((Pet*)pet)->ToggleAutocast(spellInfo, state);
else
pet->GetCharmInfo()->ToggleCreatureAutocast(spellInfo, state);
charmInfo->SetSpellAutocast(spellInfo, state);
}
void WorldSession::HandlePetCastSpellOpcode(WorldPacket& recvPacket)
{
sLog->outDetail("WORLD: CMSG_PET_CAST_SPELL");
uint64 guid;
uint8 castCount;
uint32 spellId;
uint8 castFlags;
recvPacket >> guid >> castCount >> spellId >> castFlags;
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_PET_CAST_SPELL, guid: " UI64FMTD ", castCount: %u, spellId %u, castFlags %u", guid, castCount, spellId, castFlags);
// This opcode is also sent from charmed and possessed units (players and creatures)
if (!_player->GetGuardianPet() && !_player->GetCharm())
return;
Unit* caster = ObjectAccessor::GetUnit(*_player, guid);
if (!caster || (caster != _player->GetGuardianPet() && caster != _player->GetCharm()))
{
sLog->outError("HandlePetCastSpellOpcode: Pet %u isn't pet of player %s .", uint32(GUID_LOPART(guid)), GetPlayer()->GetName());
return;
}
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
if (!spellInfo)
{
sLog->outError("WORLD: unknown PET spell id %i", spellId);
return;
}
if (spellInfo->StartRecoveryCategory > 0) // Check if spell is affected by GCD
if (caster->GetTypeId() == TYPEID_UNIT && caster->GetCharmInfo() && caster->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo))
{
caster->SendPetCastFail(spellId, SPELL_FAILED_NOT_READY);
return;
}
// do not cast not learned spells
if (!caster->HasSpell(spellId) || spellInfo->IsPassive())
return;
SpellCastTargets targets;
targets.Read(recvPacket, caster);
HandleClientCastFlags(recvPacket, castFlags, targets);
caster->ClearUnitState(UNIT_STAT_FOLLOW);
Spell* spell = new Spell(caster, spellInfo, TRIGGERED_NONE);
spell->m_cast_count = castCount; // probably pending spell cast
spell->m_targets = targets;
// TODO: need to check victim?
SpellCastResult result;
if (caster->m_movedPlayer)
result = spell->CheckPetCast(caster->m_movedPlayer->GetSelectedUnit());
else
result = spell->CheckPetCast(NULL);
if (result == SPELL_CAST_OK)
{
if (caster->GetTypeId() == TYPEID_UNIT)
{
Creature* pet = caster->ToCreature();
pet->AddCreatureSpellCooldown(spellId);
if (pet->isPet())
{
Pet* p = (Pet*)pet;
// 10% chance to play special pet attack talk, else growl
// actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell
if (p->getPetType() == SUMMON_PET && (urand(0, 100) < 10))
pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL);
else
pet->SendPetAIReaction(guid);
}
}
spell->prepare(&(spell->m_targets));
}
else
{
caster->SendPetCastFail(spellId, result);
if (caster->GetTypeId() == TYPEID_PLAYER)
{
if (!caster->ToPlayer()->HasSpellCooldown(spellId))
GetPlayer()->SendClearCooldown(spellId, caster);
}
else
{
if (!caster->ToCreature()->HasSpellCooldown(spellId))
GetPlayer()->SendClearCooldown(spellId, caster);
}
spell->finish(false);
delete spell;
}
}
void WorldSession::SendPetNameInvalid(uint32 error, const std::string& name, DeclinedName *declinedName)
{
WorldPacket data(SMSG_PET_NAME_INVALID, 4 + name.size() + 1 + 1);
data << uint32(error);
data << name;
if (declinedName)
{
data << uint8(1);
for (uint32 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
data << declinedName->name[i];
}
else
data << uint8(0);
SendPacket(&data);
}
void WorldSession::HandlePetLearnTalent(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_PET_LEARN_TALENT");
uint64 guid;
uint32 talent_id, requested_rank;
recv_data >> guid >> talent_id >> requested_rank;
_player->LearnPetTalent(guid, talent_id, requested_rank);
_player->SendTalentsInfoData(true);
}
void WorldSession::HandleLearnPreviewTalentsPet(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LEARN_PREVIEW_TALENTS_PET");
uint64 guid;
recv_data >> guid;
uint32 talentsCount;
recv_data >> talentsCount;
uint32 talentId, talentRank;
for (uint32 i = 0; i < talentsCount; ++i)
{
recv_data >> talentId >> talentRank;
_player->LearnPetTalent(guid, talentId, talentRank);
}
_player->SendTalentsInfoData(true);
}
+937
View File
@@ -0,0 +1,937 @@
/*
* Copyright (C) 2008-2012 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 "WorldPacket.h"
#include "WorldSession.h"
#include "World.h"
#include "ObjectMgr.h"
#include "ArenaTeamMgr.h"
#include "GuildMgr.h"
#include "Log.h"
#include "Opcodes.h"
#include "Guild.h"
#include "ArenaTeam.h"
#include "GossipDef.h"
#include "SocialMgr.h"
#define CHARTER_DISPLAY_ID 16161
/*enum PetitionType // dbc data
{
PETITION_TYPE_GUILD = 1,
PETITION_TYPE_ARENA_TEAM = 3
};*/
// Charters ID in item_template
enum CharterItemIDs
{
GUILD_CHARTER = 5863,
ARENA_TEAM_CHARTER_2v2 = 23560,
ARENA_TEAM_CHARTER_3v3 = 23561,
ARENA_TEAM_CHARTER_5v5 = 23562
};
enum CharterCosts
{
GUILD_CHARTER_COST = 1000,
ARENA_TEAM_CHARTER_2v2_COST = 800000,
ARENA_TEAM_CHARTER_3v3_COST = 1200000,
ARENA_TEAM_CHARTER_5v5_COST = 2000000
};
void WorldSession::HandlePetitionBuyOpcode(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "Received opcode CMSG_PETITION_BUY");
uint64 guidNPC;
uint32 clientIndex; // 1 for guild and arenaslot+1 for arenas in client
std::string name;
recv_data >> guidNPC; // NPC GUID
recv_data.read_skip<uint32>(); // 0
recv_data.read_skip<uint64>(); // 0
recv_data >> name; // name
recv_data.read_skip<std::string>(); // some string
recv_data.read_skip<uint32>(); // 0
recv_data.read_skip<uint32>(); // 0
recv_data.read_skip<uint32>(); // 0
recv_data.read_skip<uint32>(); // 0
recv_data.read_skip<uint32>(); // 0
recv_data.read_skip<uint32>(); // 0
recv_data.read_skip<uint32>(); // 0
recv_data.read_skip<uint16>(); // 0
recv_data.read_skip<uint32>(); // 0
recv_data.read_skip<uint32>(); // 0
recv_data.read_skip<uint32>(); // 0
for (int i = 0; i < 10; ++i)
recv_data.read_skip<std::string>();
recv_data >> clientIndex; // index
recv_data.read_skip<uint32>(); // 0
sLog->outDebug(LOG_FILTER_NETWORKIO, "Petitioner with GUID %u tried sell petition: name %s", GUID_LOPART(guidNPC), name.c_str());
// prevent cheating
Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(guidNPC, UNIT_NPC_FLAG_PETITIONER);
if (!creature)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandlePetitionBuyOpcode - Unit (GUID: %u) not found or you can't interact with him.", GUID_LOPART(guidNPC));
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
uint32 charterid = 0;
uint32 cost = 0;
uint32 type = 0;
if (creature->isTabardDesigner())
{
// if tabard designer, then trying to buy a guild charter.
// do not let if already in guild.
if (_player->GetGuildId())
return;
charterid = GUILD_CHARTER;
cost = GUILD_CHARTER_COST;
type = GUILD_CHARTER_TYPE;
}
else
{
// TODO: find correct opcode
if (_player->getLevel() < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
{
SendNotification(LANG_ARENA_ONE_TOOLOW, sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL));
return;
}
switch (clientIndex) // arenaSlot+1 as received from client (1 from 3 case)
{
case 1:
charterid = ARENA_TEAM_CHARTER_2v2;
cost = ARENA_TEAM_CHARTER_2v2_COST;
type = ARENA_TEAM_CHARTER_2v2_TYPE;
break;
case 2:
charterid = ARENA_TEAM_CHARTER_3v3;
cost = ARENA_TEAM_CHARTER_3v3_COST;
type = ARENA_TEAM_CHARTER_3v3_TYPE;
break;
case 3:
charterid = ARENA_TEAM_CHARTER_5v5;
cost = ARENA_TEAM_CHARTER_5v5_COST;
type = ARENA_TEAM_CHARTER_5v5_TYPE;
break;
default:
sLog->outDebug(LOG_FILTER_NETWORKIO, "unknown selection at buy arena petition: %u", clientIndex);
return;
}
if (_player->GetArenaTeamId(clientIndex - 1)) // arenaSlot+1 as received from client
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ALREADY_IN_ARENA_TEAM);
return;
}
}
if (type == GUILD_CHARTER_TYPE)
{
if (sGuildMgr->GetGuildByName(name))
{
Guild::SendCommandResult(this, GUILD_CREATE_S, ERR_GUILD_NAME_EXISTS_S, name);
return;
}
if (sObjectMgr->IsReservedName(name) || !ObjectMgr::IsValidCharterName(name))
{
Guild::SendCommandResult(this, GUILD_CREATE_S, ERR_GUILD_NAME_INVALID, name);
return;
}
}
else
{
if (sArenaTeamMgr->GetArenaTeamByName(name))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ARENA_TEAM_NAME_EXISTS_S);
return;
}
if (sObjectMgr->IsReservedName(name) || !ObjectMgr::IsValidCharterName(name))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ARENA_TEAM_NAME_INVALID);
return;
}
}
ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(charterid);
if (!pProto)
{
_player->SendBuyError(BUY_ERR_CANT_FIND_ITEM, NULL, charterid, 0);
return;
}
if (!_player->HasEnoughMoney(cost))
{ //player hasn't got enough money
_player->SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, creature, charterid, 0);
return;
}
ItemPosCountVec dest;
InventoryResult msg = _player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, charterid, pProto->BuyCount);
if (msg != EQUIP_ERR_OK)
{
_player->SendEquipError(msg, NULL, NULL, charterid);
return;
}
_player->ModifyMoney(-(int32)cost);
Item* charter = _player->StoreNewItem(dest, charterid, true);
if (!charter)
return;
charter->SetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1, charter->GetGUIDLow());
// ITEM_FIELD_ENCHANTMENT_1_1 is guild/arenateam id
// ITEM_FIELD_ENCHANTMENT_1_1+1 is current signatures count (showed on item)
charter->SetState(ITEM_CHANGED, _player);
_player->SendNewItem(charter, 1, true, false);
// a petition is invalid, if both the owner and the type matches
// we checked above, if this player is in an arenateam, so this must be
// datacorruption
QueryResult result = CharacterDatabase.PQuery("SELECT petitionguid FROM petition WHERE ownerguid = '%u' AND type = '%u'", _player->GetGUIDLow(), type);
std::ostringstream ssInvalidPetitionGUIDs;
if (result)
{
do
{
Field* fields = result->Fetch();
ssInvalidPetitionGUIDs << '\'' << fields[0].GetUInt32() << "', ";
} while (result->NextRow());
}
// delete petitions with the same guid as this one
ssInvalidPetitionGUIDs << '\'' << charter->GetGUIDLow() << '\'';
sLog->outDebug(LOG_FILTER_NETWORKIO, "Invalid petition GUIDs: %s", ssInvalidPetitionGUIDs.str().c_str());
CharacterDatabase.EscapeString(name);
SQLTransaction trans = CharacterDatabase.BeginTransaction();
trans->PAppend("DELETE FROM petition WHERE petitionguid IN (%s)", ssInvalidPetitionGUIDs.str().c_str());
trans->PAppend("DELETE FROM petition_sign WHERE petitionguid IN (%s)", ssInvalidPetitionGUIDs.str().c_str());
trans->PAppend("INSERT INTO petition (ownerguid, petitionguid, name, type) VALUES ('%u', '%u', '%s', '%u')",
_player->GetGUIDLow(), charter->GetGUIDLow(), name.c_str(), type);
CharacterDatabase.CommitTransaction(trans);
}
void WorldSession::HandlePetitionShowSignOpcode(WorldPacket & recv_data)
{
// ok
sLog->outDebug(LOG_FILTER_NETWORKIO, "Received opcode CMSG_PETITION_SHOW_SIGNATURES");
uint8 signs = 0;
uint64 petitionguid;
recv_data >> petitionguid; // petition guid
// solve (possible) some strange compile problems with explicit use GUID_LOPART(petitionguid) at some GCC versions (wrong code optimization in compiler?)
uint32 petitionguid_low = GUID_LOPART(petitionguid);
QueryResult result = CharacterDatabase.PQuery("SELECT type FROM petition WHERE petitionguid = '%u'", petitionguid_low);
if (!result)
{
sLog->outError("Petition %u is not found for player %u %s", GUID_LOPART(petitionguid), GetPlayer()->GetGUIDLow(), GetPlayer()->GetName());
return;
}
Field* fields = result->Fetch();
uint32 type = fields[0].GetUInt8();
// if guild petition and has guild => error, return;
if (type == GUILD_CHARTER_TYPE && _player->GetGuildId())
return;
result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE petitionguid = '%u'", petitionguid_low);
// result == NULL also correct in case no sign yet
if (result)
signs = uint8(result->GetRowCount());
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_PETITION_SHOW_SIGNATURES petition entry: '%u'", petitionguid_low);
WorldPacket data(SMSG_PETITION_SHOW_SIGNATURES, (8+8+4+1+signs*12));
data << uint64(petitionguid); // petition guid
data << uint64(_player->GetGUID()); // owner guid
data << uint32(petitionguid_low); // guild guid
data << uint8(signs); // sign's count
for (uint8 i = 1; i <= signs; ++i)
{
Field* fields2 = result->Fetch();
uint64 plguid = fields2[0].GetUInt64();
data << uint64(plguid); // Player GUID
data << uint32(0); // there 0 ...
result->NextRow();
}
SendPacket(&data);
}
void WorldSession::HandlePetitionQueryOpcode(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "Received opcode CMSG_PETITION_QUERY"); // ok
uint32 guildguid;
uint64 petitionguid;
recv_data >> guildguid; // in Trinity always same as GUID_LOPART(petitionguid)
recv_data >> petitionguid; // petition guid
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_PETITION_QUERY Petition GUID %u Guild GUID %u", GUID_LOPART(petitionguid), guildguid);
SendPetitionQueryOpcode(petitionguid);
}
void WorldSession::SendPetitionQueryOpcode(uint64 petitionguid)
{
uint64 ownerguid = 0;
uint32 type;
std::string name = "NO_NAME_FOR_GUID";
// TODO: Use CHAR_LOAD_PETITION PS
QueryResult result = CharacterDatabase.PQuery("SELECT ownerguid, name, type "
"FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
if (result)
{
Field* fields = result->Fetch();
ownerguid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
name = fields[1].GetString();
type = fields[2].GetUInt32();
}
else
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_PETITION_QUERY failed for petition (GUID: %u)", GUID_LOPART(petitionguid));
return;
}
WorldPacket data(SMSG_PETITION_QUERY_RESPONSE, (4+8+name.size()+1+1+4*12+2+10));
data << uint32(GUID_LOPART(petitionguid)); // guild/team guid (in Trinity always same as GUID_LOPART(petition guid)
data << uint64(ownerguid); // charter owner guid
data << name; // name (guild/arena team)
data << uint8(0); // some string
if (type == GUILD_CHARTER_TYPE)
{
data << uint32(9);
data << uint32(9);
data << uint32(0); // bypass client - side limitation, a different value is needed here for each petition
}
else
{
data << uint32(type-1);
data << uint32(type-1);
data << uint32(type); // bypass client - side limitation, a different value is needed here for each petition
}
data << uint32(0); // 5
data << uint32(0); // 6
data << uint32(0); // 7
data << uint32(0); // 8
data << uint16(0); // 9 2 bytes field
data << uint32(0); // 10
data << uint32(0); // 11
data << uint32(0); // 13 count of next strings?
for (int i = 0; i < 10; ++i)
data << uint8(0); // some string
data << uint32(0); // 14
if (type == GUILD_CHARTER_TYPE)
data << uint32(0); // 15 0 - guild, 1 - arena team
else
data << uint32(1);
SendPacket(&data);
}
void WorldSession::HandlePetitionRenameOpcode(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "Received opcode MSG_PETITION_RENAME"); // ok
uint64 petitionGuid;
uint32 type;
std::string newName;
recv_data >> petitionGuid; // guid
recv_data >> newName; // new name
Item* item = _player->GetItemByGuid(petitionGuid);
if (!item)
return;
QueryResult result = CharacterDatabase.PQuery("SELECT type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionGuid));
if (result)
{
Field* fields = result->Fetch();
type = fields[0].GetUInt8();
}
else
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_PETITION_QUERY failed for petition (GUID: %u)", GUID_LOPART(petitionGuid));
return;
}
if (type == GUILD_CHARTER_TYPE)
{
if (sGuildMgr->GetGuildByName(newName))
{
Guild::SendCommandResult(this, GUILD_CREATE_S, ERR_GUILD_NAME_EXISTS_S, newName);
return;
}
if (sObjectMgr->IsReservedName(newName) || !ObjectMgr::IsValidCharterName(newName))
{
Guild::SendCommandResult(this, GUILD_CREATE_S, ERR_GUILD_NAME_INVALID, newName);
return;
}
}
else
{
if (sArenaTeamMgr->GetArenaTeamByName(newName))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, newName, "", ERR_ARENA_TEAM_NAME_EXISTS_S);
return;
}
if (sObjectMgr->IsReservedName(newName) || !ObjectMgr::IsValidCharterName(newName))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, newName, "", ERR_ARENA_TEAM_NAME_INVALID);
return;
}
}
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_PETITION_NAME);
stmt->setString(0, newName);
stmt->setUInt32(1, GUID_LOPART(petitionGuid));
CharacterDatabase.Execute(stmt);
sLog->outDebug(LOG_FILTER_NETWORKIO, "Petition (GUID: %u) renamed to '%s'", GUID_LOPART(petitionGuid), newName.c_str());
WorldPacket data(MSG_PETITION_RENAME, (8+newName.size()+1));
data << uint64(petitionGuid);
data << newName;
SendPacket(&data);
}
void WorldSession::HandlePetitionSignOpcode(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "Received opcode CMSG_PETITION_SIGN"); // ok
Field* fields;
uint64 petitionGuid;
uint8 unk;
recv_data >> petitionGuid; // petition guid
recv_data >> unk;
QueryResult result = CharacterDatabase.PQuery(
"SELECT ownerguid, "
" (SELECT COUNT(playerguid) FROM petition_sign WHERE petition_sign.petitionguid = '%u') AS signs, "
" type "
"FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionGuid), GUID_LOPART(petitionGuid));
if (!result)
{
sLog->outError("Petition %u is not found for player %u %s", GUID_LOPART(petitionGuid), GetPlayer()->GetGUIDLow(), GetPlayer()->GetName());
return;
}
fields = result->Fetch();
uint64 ownerGuid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
uint8 signs = fields[1].GetUInt8();
uint32 type = fields[2].GetUInt32();
uint32 playerGuid = _player->GetGUIDLow();
if (GUID_LOPART(ownerGuid) == playerGuid)
return;
// not let enemies sign guild charter
if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && GetPlayer()->GetTeam() != sObjectMgr->GetPlayerTeamByGUID(ownerGuid))
{
if (type != GUILD_CHARTER_TYPE)
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED);
else
Guild::SendCommandResult(this, GUILD_CREATE_S, ERR_GUILD_NOT_ALLIED);
return;
}
if (type != GUILD_CHARTER_TYPE)
{
if (_player->getLevel() < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", _player->GetName(), ERR_ARENA_TEAM_TARGET_TOO_LOW_S);
return;
}
uint8 slot = ArenaTeam::GetSlotByType(type);
if (slot >= MAX_ARENA_SLOT)
return;
if (_player->GetArenaTeamId(slot))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", _player->GetName(), ERR_ALREADY_IN_ARENA_TEAM_S);
return;
}
if (_player->GetArenaTeamIdInvited())
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", _player->GetName(), ERR_ALREADY_INVITED_TO_ARENA_TEAM_S);
return;
}
}
else
{
if (_player->GetGuildId())
{
Guild::SendCommandResult(this, GUILD_INVITE_S, ERR_ALREADY_IN_GUILD_S, _player->GetName());
return;
}
if (_player->GetGuildIdInvited())
{
Guild::SendCommandResult(this, GUILD_INVITE_S, ERR_ALREADY_INVITED_TO_GUILD_S, _player->GetName());
return;
}
}
if (++signs > type) // client signs maximum
return;
//client doesn't allow to sign petition two times by one character, but not check sign by another character from same account
//not allow sign another player from already sign player account
result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE player_account = '%u' AND petitionguid = '%u'", GetAccountId(), GUID_LOPART(petitionGuid));
if (result)
{
WorldPacket data(SMSG_PETITION_SIGN_RESULTS, (8+8+4));
data << uint64(petitionGuid);
data << uint64(_player->GetGUID());
data << (uint32)PETITION_SIGN_ALREADY_SIGNED;
// close at signer side
SendPacket(&data);
// update for owner if online
if (Player* owner = ObjectAccessor::FindPlayer(ownerGuid))
owner->GetSession()->SendPacket(&data);
return;
}
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PETITION_SIGNATURE);
stmt->setUInt32(0, GUID_LOPART(ownerGuid));
stmt->setUInt32(1, GUID_LOPART(petitionGuid));
stmt->setUInt32(2, playerGuid);
stmt->setUInt32(3, GetAccountId());
CharacterDatabase.Execute(stmt);
sLog->outDebug(LOG_FILTER_NETWORKIO, "PETITION SIGN: GUID %u by player: %s (GUID: %u Account: %u)", GUID_LOPART(petitionGuid), _player->GetName(), playerGuid, GetAccountId());
WorldPacket data(SMSG_PETITION_SIGN_RESULTS, (8+8+4));
data << uint64(petitionGuid);
data << uint64(_player->GetGUID());
data << uint32(PETITION_SIGN_OK);
// close at signer side
SendPacket(&data);
// update signs count on charter, required testing...
//Item* item = _player->GetItemByGuid(petitionguid));
//if (item)
// item->SetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1+1, signs);
// update for owner if online
if (Player* owner = ObjectAccessor::FindPlayer(ownerGuid))
owner->GetSession()->SendPacket(&data);
}
void WorldSession::HandlePetitionDeclineOpcode(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "Received opcode MSG_PETITION_DECLINE"); // ok
uint64 petitionguid;
uint64 ownerguid;
recv_data >> petitionguid; // petition guid
sLog->outDebug(LOG_FILTER_NETWORKIO, "Petition %u declined by %u", GUID_LOPART(petitionguid), _player->GetGUIDLow());
QueryResult result = CharacterDatabase.PQuery("SELECT ownerguid FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
if (!result)
return;
Field* fields = result->Fetch();
ownerguid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
Player* owner = ObjectAccessor::FindPlayer(ownerguid);
if (owner) // petition owner online
{
WorldPacket data(MSG_PETITION_DECLINE, 8);
data << uint64(_player->GetGUID());
owner->GetSession()->SendPacket(&data);
}
}
void WorldSession::HandleOfferPetitionOpcode(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "Received opcode CMSG_OFFER_PETITION"); // ok
uint8 signs = 0;
uint64 petitionguid, plguid;
uint32 type, junk;
Player* player;
recv_data >> junk; // this is not petition type!
recv_data >> petitionguid; // petition guid
recv_data >> plguid; // player guid
player = ObjectAccessor::FindPlayer(plguid);
if (!player)
return;
QueryResult result = CharacterDatabase.PQuery("SELECT type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
if (!result)
return;
Field* fields = result->Fetch();
type = fields[0].GetUInt8();
sLog->outDebug(LOG_FILTER_NETWORKIO, "OFFER PETITION: type %u, GUID1 %u, to player id: %u", type, GUID_LOPART(petitionguid), GUID_LOPART(plguid));
if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && GetPlayer()->GetTeam() != player->GetTeam())
{
if (type != GUILD_CHARTER_TYPE)
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED);
else
Guild::SendCommandResult(this, GUILD_CREATE_S, ERR_GUILD_NOT_ALLIED);
return;
}
if (type != GUILD_CHARTER_TYPE)
{
if (player->getLevel() < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
{
// player is too low level to join an arena team
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, player->GetName(), "", ERR_ARENA_TEAM_TARGET_TOO_LOW_S);
return;
}
uint8 slot = ArenaTeam::GetSlotByType(type);
if (slot >= MAX_ARENA_SLOT)
return;
if (player->GetArenaTeamId(slot))
{
// player is already in an arena team
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, player->GetName(), "", ERR_ALREADY_IN_ARENA_TEAM_S);
return;
}
if (player->GetArenaTeamIdInvited())
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", _player->GetName(), ERR_ALREADY_INVITED_TO_ARENA_TEAM_S);
return;
}
}
else
{
if (player->GetGuildId())
{
Guild::SendCommandResult(this, GUILD_INVITE_S, ERR_ALREADY_IN_GUILD_S, _player->GetName());
return;
}
if (player->GetGuildIdInvited())
{
Guild::SendCommandResult(this, GUILD_INVITE_S, ERR_ALREADY_INVITED_TO_GUILD_S, _player->GetName());
return;
}
}
result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
// result == NULL also correct charter without signs
if (result)
signs = uint8(result->GetRowCount());
WorldPacket data(SMSG_PETITION_SHOW_SIGNATURES, (8+8+4+signs+signs*12));
data << uint64(petitionguid); // petition guid
data << uint64(_player->GetGUID()); // owner guid
data << uint32(GUID_LOPART(petitionguid)); // guild guid
data << uint8(signs); // sign's count
for (uint8 i = 1; i <= signs; ++i)
{
Field* fields2 = result->Fetch();
plguid = fields2[0].GetUInt64();
data << uint64(plguid); // Player GUID
data << uint32(0); // there 0 ...
result->NextRow();
}
player->GetSession()->SendPacket(&data);
}
void WorldSession::HandleTurnInPetitionOpcode(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "Received opcode CMSG_TURN_IN_PETITION");
// Get petition guid from packet
WorldPacket data;
uint64 petitionGuid;
recv_data >> petitionGuid;
// Check if player really has the required petition charter
Item* item = _player->GetItemByGuid(petitionGuid);
if (!item)
return;
sLog->outDebug(LOG_FILTER_NETWORKIO, "Petition %u turned in by %u", GUID_LOPART(petitionGuid), _player->GetGUIDLow());
// Get petition data from db
uint32 ownerguidlo;
uint32 type;
std::string name;
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PETITION);
stmt->setUInt32(0, GUID_LOPART(petitionGuid));
PreparedQueryResult result = CharacterDatabase.Query(stmt);
if (result)
{
Field* fields = result->Fetch();
ownerguidlo = fields[0].GetUInt32();
name = fields[1].GetString();
type = fields[2].GetUInt8();
}
else
{
sLog->outError("Player %s (guid: %u) tried to turn in petition (guid: %u) that is not present in the database", _player->GetName(), _player->GetGUIDLow(), GUID_LOPART(petitionGuid));
return;
}
// Only the petition owner can turn in the petition
if (_player->GetGUIDLow() != ownerguidlo)
return;
// Petition type (guild/arena) specific checks
if (type == GUILD_CHARTER_TYPE)
{
// Check if player is already in a guild
if (_player->GetGuildId())
{
data.Initialize(SMSG_TURN_IN_PETITION_RESULTS, 4);
data << (uint32)PETITION_TURN_ALREADY_IN_GUILD;
_player->GetSession()->SendPacket(&data);
return;
}
// Check if guild name is already taken
if (sGuildMgr->GetGuildByName(name))
{
Guild::SendCommandResult(this, GUILD_CREATE_S, ERR_GUILD_NAME_EXISTS_S, name);
return;
}
}
else
{
// Check for valid arena bracket (2v2, 3v3, 5v5)
uint8 slot = ArenaTeam::GetSlotByType(type);
if (slot >= MAX_ARENA_SLOT)
return;
// Check if player is already in an arena team
if (_player->GetArenaTeamId(slot))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ALREADY_IN_ARENA_TEAM);
return;
}
// Check if arena team name is already taken
if (sArenaTeamMgr->GetArenaTeamByName(name))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ARENA_TEAM_NAME_EXISTS_S);
return;
}
}
// Get petition signatures from db
uint8 signatures;
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PETITION_SIGNATURE);
stmt->setUInt32(0, GUID_LOPART(petitionGuid));
result = CharacterDatabase.Query(stmt);
if (result)
signatures = uint8(result->GetRowCount());
else
signatures = 0;
uint32 requiredSignatures;
if (type == GUILD_CHARTER_TYPE)
requiredSignatures = sWorld->getIntConfig(CONFIG_MIN_PETITION_SIGNS);
else
requiredSignatures = type-1;
// Notify player if signatures are missing
if (signatures < requiredSignatures)
{
data.Initialize(SMSG_TURN_IN_PETITION_RESULTS, 4);
data << (uint32)PETITION_TURN_NEED_MORE_SIGNATURES;
SendPacket(&data);
return;
}
// Proceed with guild/arena team creation
// Delete charter item
_player->DestroyItem(item->GetBagSlot(), item->GetSlot(), true);
if (type == GUILD_CHARTER_TYPE)
{
// Create guild
Guild* guild = new Guild;
if (!guild->Create(_player, name))
{
delete guild;
return;
}
// Register guild and add guild master
sGuildMgr->AddGuild(guild);
// Add members from signatures
for (uint8 i = 0; i < signatures; ++i)
{
Field* fields = result->Fetch();
guild->AddMember(MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER));
result->NextRow();
}
}
else
{
// Receive the rest of the packet in arena team creation case
uint32 background, icon, iconcolor, border, bordercolor;
recv_data >> background >> icon >> iconcolor >> border >> bordercolor;
// Create arena team
ArenaTeam* arenaTeam = new ArenaTeam();
if (!arenaTeam->Create(_player->GetGUID(), type, name, background, icon, iconcolor, border, bordercolor))
{
delete arenaTeam;
return;
}
// Register arena team
sArenaTeamMgr->AddArenaTeam(arenaTeam);
sLog->outDebug(LOG_FILTER_NETWORKIO, "PetitonsHandler: Arena team (guid: %u) added to ObjectMgr", arenaTeam->GetId());
// Add members
for (uint8 i = 0; i < signatures; ++i)
{
Field* fields = result->Fetch();
uint32 memberGUID = fields[0].GetUInt32();
sLog->outDebug(LOG_FILTER_NETWORKIO, "PetitionsHandler: Adding arena team (guid: %u) member %u", arenaTeam->GetId(), memberGUID);
arenaTeam->AddMember(MAKE_NEW_GUID(memberGUID, 0, HIGHGUID_PLAYER));
result->NextRow();
}
}
SQLTransaction trans = CharacterDatabase.BeginTransaction();
trans->PAppend("DELETE FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionGuid));
trans->PAppend("DELETE FROM petition_sign WHERE petitionguid = '%u'", GUID_LOPART(petitionGuid));
CharacterDatabase.CommitTransaction(trans);
// created
sLog->outDebug(LOG_FILTER_NETWORKIO, "TURN IN PETITION GUID %u", GUID_LOPART(petitionGuid));
data.Initialize(SMSG_TURN_IN_PETITION_RESULTS, 4);
data << (uint32)PETITION_TURN_OK;
SendPacket(&data);
}
void WorldSession::HandlePetitionShowListOpcode(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "Received CMSG_PETITION_SHOWLIST");
uint64 guid;
recv_data >> guid;
SendPetitionShowList(guid);
}
void WorldSession::SendPetitionShowList(uint64 guid)
{
Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_PETITIONER);
if (!creature)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandlePetitionShowListOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)));
return;
}
WorldPacket data(SMSG_PETITION_SHOWLIST, 8+1+4*6);
data << guid; // npc guid
if (creature->isTabardDesigner())
{
data << uint8(1); // count
data << uint32(1); // index
data << uint32(GUILD_CHARTER); // charter entry
data << uint32(CHARTER_DISPLAY_ID); // charter display id
data << uint32(GUILD_CHARTER_COST); // charter cost
data << uint32(0); // unknown
data << uint32(9); // required signs?
}
else
{
data << uint8(3); // count
// 2v2
data << uint32(1); // index
data << uint32(ARENA_TEAM_CHARTER_2v2); // charter entry
data << uint32(CHARTER_DISPLAY_ID); // charter display id
data << uint32(ARENA_TEAM_CHARTER_2v2_COST); // charter cost
data << uint32(2); // unknown
data << uint32(2); // required signs?
// 3v3
data << uint32(2); // index
data << uint32(ARENA_TEAM_CHARTER_3v3); // charter entry
data << uint32(CHARTER_DISPLAY_ID); // charter display id
data << uint32(ARENA_TEAM_CHARTER_3v3_COST); // charter cost
data << uint32(3); // unknown
data << uint32(3); // required signs?
// 5v5
data << uint32(3); // index
data << uint32(ARENA_TEAM_CHARTER_5v5); // charter entry
data << uint32(CHARTER_DISPLAY_ID); // charter display id
data << uint32(ARENA_TEAM_CHARTER_5v5_COST); // charter cost
data << uint32(5); // unknown
data << uint32(5); // required signs?
}
SendPacket(&data);
sLog->outDebug(LOG_FILTER_NETWORKIO, "Sent SMSG_PETITION_SHOWLIST");
}
+477
View File
@@ -0,0 +1,477 @@
/*
* Copyright (C) 2008-2012 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 "WorldSession.h"
#include "Opcodes.h"
#include "Log.h"
#include "World.h"
#include "ObjectMgr.h"
#include "Player.h"
#include "UpdateMask.h"
#include "NPCHandler.h"
#include "Pet.h"
#include "MapManager.h"
void WorldSession::SendNameQueryOpcode(uint64 guid)
{
Player* player = NULL;
const CharacterNameData* nameData = sWorld->GetCharacterNameData(GUID_LOPART(guid));
if (nameData)
player = ObjectAccessor::FindPlayer(guid);
// guess size
WorldPacket data(SMSG_NAME_QUERY_RESPONSE, (8+1+1+1+1+1+10));
data.appendPackGUID(guid);
data << uint8(0); // added in 3.1
if (nameData)
{
data << nameData->m_name; // played name
data << uint8(0); // realm name for cross realm BG usage
data << uint8(nameData->m_race);
data << uint8(nameData->m_gender);
data << uint8(nameData->m_class);
}
else
{
data << std::string(GetTrinityString(LANG_NON_EXIST_CHARACTER));
data << uint32(0);
}
if (player)
{
if (DeclinedName const* names = player->GetDeclinedNames())
{
data << uint8(1); // is declined
for (int i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
data << names->name[i];
}
else
data << uint8(0); // is not declined
}
else //TODO: decline names may also need to be stored in char name data
data << uint8(0);
SendPacket(&data);
}
void WorldSession::HandleNameQueryOpcode(WorldPacket& recv_data)
{
uint64 guid;
recv_data >> guid;
// This is disable by default to prevent lots of console spam
// sLog->outString("HandleNameQueryOpcode %u", guid);
SendNameQueryOpcode(guid);
}
void WorldSession::HandleQueryTimeOpcode(WorldPacket & /*recv_data*/)
{
SendQueryTimeResponse();
}
void WorldSession::SendQueryTimeResponse()
{
WorldPacket data(SMSG_QUERY_TIME_RESPONSE, 4+4);
data << uint32(time(NULL));
data << uint32(sWorld->GetNextDailyQuestsResetTime() - time(NULL));
SendPacket(&data);
}
/// Only _static_ data is sent in this packet !!!
void WorldSession::HandleCreatureQueryOpcode(WorldPacket & recv_data)
{
uint32 entry;
recv_data >> entry;
uint64 guid;
recv_data >> guid;
CreatureTemplate const* ci = sObjectMgr->GetCreatureTemplate(entry);
if (ci)
{
std::string Name, SubName;
Name = ci->Name;
SubName = ci->SubName;
int loc_idx = GetSessionDbLocaleIndex();
if (loc_idx >= 0)
{
if (CreatureLocale const* cl = sObjectMgr->GetCreatureLocale(entry))
{
ObjectMgr::GetLocaleString(cl->Name, loc_idx, Name);
ObjectMgr::GetLocaleString(cl->SubName, loc_idx, SubName);
}
}
sLog->outDetail("WORLD: CMSG_CREATURE_QUERY '%s' - Entry: %u.", ci->Name.c_str(), entry);
// guess size
WorldPacket data(SMSG_CREATURE_QUERY_RESPONSE, 100);
data << uint32(entry); // creature entry
data << Name;
data << uint8(0) << uint8(0) << uint8(0); // name2, name3, name4, always empty
data << SubName;
data << ci->IconName; // "Directions" for guard, string for Icons 2.3.0
data << uint32(ci->type_flags); // flags
data << uint32(ci->type); // CreatureType.dbc
data << uint32(ci->family); // CreatureFamily.dbc
data << uint32(ci->rank); // Creature Rank (elite, boss, etc)
data << uint32(ci->KillCredit[0]); // new in 3.1, kill credit
data << uint32(ci->KillCredit[1]); // new in 3.1, kill credit
data << uint32(ci->Modelid1); // Modelid1
data << uint32(ci->Modelid2); // Modelid2
data << uint32(ci->Modelid3); // Modelid3
data << uint32(ci->Modelid4); // Modelid4
data << float(ci->ModHealth); // dmg/hp modifier
data << float(ci->ModMana); // dmg/mana modifier
data << uint8(ci->RacialLeader);
for (uint32 i = 0; i < MAX_CREATURE_QUEST_ITEMS; ++i)
data << uint32(ci->questItems[i]); // itemId[6], quest drop
data << uint32(ci->movementId); // CreatureMovementInfo.dbc
SendPacket(&data);
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_CREATURE_QUERY_RESPONSE");
}
else
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CREATURE_QUERY - NO CREATURE INFO! (GUID: %u, ENTRY: %u)",
GUID_LOPART(guid), entry);
WorldPacket data(SMSG_CREATURE_QUERY_RESPONSE, 4);
data << uint32(entry | 0x80000000);
SendPacket(&data);
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_CREATURE_QUERY_RESPONSE");
}
}
/// Only _static_ data is sent in this packet !!!
void WorldSession::HandleGameObjectQueryOpcode(WorldPacket & recv_data)
{
uint32 entry;
recv_data >> entry;
uint64 guid;
recv_data >> guid;
const GameObjectTemplate* info = sObjectMgr->GetGameObjectTemplate(entry);
if (info)
{
std::string Name;
std::string IconName;
std::string CastBarCaption;
Name = info->name;
IconName = info->IconName;
CastBarCaption = info->castBarCaption;
int loc_idx = GetSessionDbLocaleIndex();
if (loc_idx >= 0)
{
if (GameObjectLocale const* gl = sObjectMgr->GetGameObjectLocale(entry))
{
ObjectMgr::GetLocaleString(gl->Name, loc_idx, Name);
ObjectMgr::GetLocaleString(gl->CastBarCaption, loc_idx, CastBarCaption);
}
}
sLog->outDetail("WORLD: CMSG_GAMEOBJECT_QUERY '%s' - Entry: %u. ", info->name.c_str(), entry);
WorldPacket data (SMSG_GAMEOBJECT_QUERY_RESPONSE, 150);
data << uint32(entry);
data << uint32(info->type);
data << uint32(info->displayId);
data << Name;
data << uint8(0) << uint8(0) << uint8(0); // name2, name3, name4
data << IconName; // 2.0.3, string. Icon name to use instead of default icon for go's (ex: "Attack" makes sword)
data << CastBarCaption; // 2.0.3, string. Text will appear in Cast Bar when using GO (ex: "Collecting")
data << info->unk1; // 2.0.3, string
data.append(info->raw.data, 24);
data << float(info->size); // go size
for (uint32 i = 0; i < MAX_GAMEOBJECT_QUEST_ITEMS; ++i)
data << uint32(info->questItems[i]); // itemId[6], quest drop
SendPacket(&data);
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_GAMEOBJECT_QUERY_RESPONSE");
}
else
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_GAMEOBJECT_QUERY - Missing gameobject info for (GUID: %u, ENTRY: %u)",
GUID_LOPART(guid), entry);
WorldPacket data (SMSG_GAMEOBJECT_QUERY_RESPONSE, 4);
data << uint32(entry | 0x80000000);
SendPacket(&data);
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_GAMEOBJECT_QUERY_RESPONSE");
}
}
void WorldSession::HandleCorpseQueryOpcode(WorldPacket & /*recv_data*/)
{
sLog->outDetail("WORLD: Received MSG_CORPSE_QUERY");
Corpse* corpse = GetPlayer()->GetCorpse();
if (!corpse)
{
WorldPacket data(MSG_CORPSE_QUERY, 1);
data << uint8(0); // corpse not found
SendPacket(&data);
return;
}
uint32 mapid = corpse->GetMapId();
float x = corpse->GetPositionX();
float y = corpse->GetPositionY();
float z = corpse->GetPositionZ();
uint32 corpsemapid = mapid;
// if corpse at different map
if (mapid != _player->GetMapId())
{
// search entrance map for proper show entrance
if (MapEntry const* corpseMapEntry = sMapStore.LookupEntry(mapid))
{
if (corpseMapEntry->IsDungeon() && corpseMapEntry->entrance_map >= 0)
{
// if corpse map have entrance
if (Map const* entranceMap = sMapMgr->CreateBaseMap(corpseMapEntry->entrance_map))
{
mapid = corpseMapEntry->entrance_map;
x = corpseMapEntry->entrance_x;
y = corpseMapEntry->entrance_y;
z = entranceMap->GetHeight(x, y, MAX_HEIGHT);
}
}
}
}
WorldPacket data(MSG_CORPSE_QUERY, 1+(6*4));
data << uint8(1); // corpse found
data << int32(mapid);
data << float(x);
data << float(y);
data << float(z);
data << int32(corpsemapid);
data << uint32(0); // unknown
SendPacket(&data);
}
void WorldSession::HandleNpcTextQueryOpcode(WorldPacket & recv_data)
{
uint32 textID;
uint64 guid;
recv_data >> textID;
sLog->outDetail("WORLD: CMSG_NPC_TEXT_QUERY ID '%u'", textID);
recv_data >> guid;
GetPlayer()->SetSelection(guid);
GossipText const* pGossip = sObjectMgr->GetGossipText(textID);
WorldPacket data(SMSG_NPC_TEXT_UPDATE, 100); // guess size
data << textID;
if (!pGossip)
{
for (uint32 i = 0; i < MAX_GOSSIP_TEXT_OPTIONS; ++i)
{
data << float(0);
data << "Greetings $N";
data << "Greetings $N";
data << uint32(0);
data << uint32(0);
data << uint32(0);
data << uint32(0);
data << uint32(0);
data << uint32(0);
data << uint32(0);
}
}
else
{
std::string Text_0[MAX_LOCALES], Text_1[MAX_LOCALES];
for (int i = 0; i < MAX_GOSSIP_TEXT_OPTIONS; ++i)
{
Text_0[i]=pGossip->Options[i].Text_0;
Text_1[i]=pGossip->Options[i].Text_1;
}
int loc_idx = GetSessionDbLocaleIndex();
if (loc_idx >= 0)
{
if (NpcTextLocale const* nl = sObjectMgr->GetNpcTextLocale(textID))
{
for (int i = 0; i < MAX_LOCALES; ++i)
{
ObjectMgr::GetLocaleString(nl->Text_0[i], loc_idx, Text_0[i]);
ObjectMgr::GetLocaleString(nl->Text_1[i], loc_idx, Text_1[i]);
}
}
}
for (int i = 0; i < MAX_GOSSIP_TEXT_OPTIONS; ++i)
{
data << pGossip->Options[i].Probability;
if (Text_0[i].empty())
data << Text_1[i];
else
data << Text_0[i];
if (Text_1[i].empty())
data << Text_0[i];
else
data << Text_1[i];
data << pGossip->Options[i].Language;
for (int j = 0; j < MAX_GOSSIP_TEXT_EMOTES; ++j)
{
data << pGossip->Options[i].Emotes[j]._Delay;
data << pGossip->Options[i].Emotes[j]._Emote;
}
}
}
SendPacket(&data);
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_NPC_TEXT_UPDATE");
}
/// Only _static_ data is sent in this packet !!!
void WorldSession::HandlePageTextQueryOpcode(WorldPacket & recv_data)
{
sLog->outDetail("WORLD: Received CMSG_PAGE_TEXT_QUERY");
uint32 pageID;
recv_data >> pageID;
recv_data.read_skip<uint64>(); // guid
while (pageID)
{
PageText const* pageText = sObjectMgr->GetPageText(pageID);
// guess size
WorldPacket data(SMSG_PAGE_TEXT_QUERY_RESPONSE, 50);
data << pageID;
if (!pageText)
{
data << "Item page missing.";
data << uint32(0);
pageID = 0;
}
else
{
std::string Text = pageText->Text;
int loc_idx = GetSessionDbLocaleIndex();
if (loc_idx >= 0)
if (PageTextLocale const* player = sObjectMgr->GetPageTextLocale(pageID))
ObjectMgr::GetLocaleString(player->Text, loc_idx, Text);
data << Text;
data << uint32(pageText->NextPage);
pageID = pageText->NextPage;
}
SendPacket(&data);
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_PAGE_TEXT_QUERY_RESPONSE");
}
}
void WorldSession::HandleCorpseMapPositionQuery(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recv CMSG_CORPSE_MAP_POSITION_QUERY");
uint32 unk;
recv_data >> unk;
WorldPacket data(SMSG_CORPSE_MAP_POSITION_QUERY_RESPONSE, 4+4+4+4);
data << float(0);
data << float(0);
data << float(0);
data << float(0);
SendPacket(&data);
}
void WorldSession::HandleQuestPOIQuery(WorldPacket& recv_data)
{
uint32 count;
recv_data >> count; // quest count, max=25
if (count >= MAX_QUEST_LOG_SIZE)
{
recv_data.rfinish();
return;
}
WorldPacket data(SMSG_QUEST_POI_QUERY_RESPONSE, 4+(4+4)*count);
data << uint32(count); // count
for (uint32 i = 0; i < count; ++i)
{
uint32 questId;
recv_data >> questId; // quest id
bool questOk = false;
uint16 questSlot = _player->FindQuestSlot(questId);
if (questSlot != MAX_QUEST_LOG_SIZE)
questOk =_player->GetQuestSlotQuestId(questSlot) == questId;
if (questOk)
{
QuestPOIVector const* POI = sObjectMgr->GetQuestPOIVector(questId);
if (POI)
{
data << uint32(questId); // quest ID
data << uint32(POI->size()); // POI count
for (QuestPOIVector::const_iterator itr = POI->begin(); itr != POI->end(); ++itr)
{
data << uint32(itr->Id); // POI index
data << int32(itr->ObjectiveIndex); // objective index
data << uint32(itr->MapId); // mapid
data << uint32(itr->AreaId); // areaid
data << uint32(itr->Unk2); // unknown
data << uint32(itr->Unk3); // unknown
data << uint32(itr->Unk4); // unknown
data << uint32(itr->points.size()); // POI points count
for (std::vector<QuestPOIPoint>::const_iterator itr2 = itr->points.begin(); itr2 != itr->points.end(); ++itr2)
{
data << int32(itr2->x); // POI point x
data << int32(itr2->y); // POI point y
}
}
}
else
{
data << uint32(questId); // quest ID
data << uint32(0); // POI count
}
}
else
{
data << uint32(questId); // quest ID
data << uint32(0); // POI count
}
}
SendPacket(&data);
}
+779
View File
@@ -0,0 +1,779 @@
/*
* Copyright (C) 2008-2012 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 "Log.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "Opcodes.h"
#include "World.h"
#include "ObjectMgr.h"
#include "Player.h"
#include "GossipDef.h"
#include "QuestDef.h"
#include "ObjectAccessor.h"
#include "Group.h"
#include "Battleground.h"
#include "BattlegroundAV.h"
#include "ScriptMgr.h"
#include "GameObjectAI.h"
void WorldSession::HandleQuestgiverStatusQueryOpcode(WorldPacket & recv_data)
{
uint64 guid;
recv_data >> guid;
uint8 questStatus = DIALOG_STATUS_NONE;
uint8 defstatus = DIALOG_STATUS_NONE;
Object* questgiver = ObjectAccessor::GetObjectByTypeMask(*_player, guid, TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT);
if (!questgiver)
{
sLog->outDetail("Error in CMSG_QUESTGIVER_STATUS_QUERY, called for not found questgiver (Typeid: %u GUID: %u)", GuidHigh2TypeId(GUID_HIPART(guid)), GUID_LOPART(guid));
return;
}
switch (questgiver->GetTypeId())
{
case TYPEID_UNIT:
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTGIVER_STATUS_QUERY for npc, guid = %u", uint32(GUID_LOPART(guid)));
Creature* cr_questgiver=questgiver->ToCreature();
if (!cr_questgiver->IsHostileTo(_player)) // not show quest status to enemies
{
questStatus = sScriptMgr->GetDialogStatus(_player, cr_questgiver);
if (questStatus > 6)
questStatus = getDialogStatus(_player, cr_questgiver, defstatus);
}
break;
}
case TYPEID_GAMEOBJECT:
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTGIVER_STATUS_QUERY for GameObject guid = %u", uint32(GUID_LOPART(guid)));
GameObject* go_questgiver=(GameObject*)questgiver;
questStatus = sScriptMgr->GetDialogStatus(_player, go_questgiver);
if (questStatus > 6)
questStatus = getDialogStatus(_player, go_questgiver, defstatus);
break;
}
default:
sLog->outError("QuestGiver called for unexpected type %u", questgiver->GetTypeId());
break;
}
//inform client about status of quest
_player->PlayerTalkClass->SendQuestGiverStatus(questStatus, guid);
}
void WorldSession::HandleQuestgiverHelloOpcode(WorldPacket & recv_data)
{
uint64 guid;
recv_data >> guid;
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTGIVER_HELLO npc = %u", GUID_LOPART(guid));
Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE);
if (!creature)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleQuestgiverHelloOpcode - Unit (GUID: %u) not found or you can't interact with him.",
GUID_LOPART(guid));
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
// Stop the npc if moving
creature->StopMoving();
if (sScriptMgr->OnGossipHello(_player, creature))
return;
_player->PrepareGossipMenu(creature, creature->GetCreatureInfo()->GossipMenuId, true);
_player->SendPreparedGossip(creature);
creature->AI()->sGossipHello(_player);
}
void WorldSession::HandleQuestgiverAcceptQuestOpcode(WorldPacket & recv_data)
{
uint64 guid;
uint32 quest;
uint32 unk1;
recv_data >> guid >> quest >> unk1;
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTGIVER_ACCEPT_QUEST npc = %u, quest = %u, unk1 = %u", uint32(GUID_LOPART(guid)), quest, unk1);
Object* pObject = ObjectAccessor::GetObjectByTypeMask(*_player, guid, TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT|TYPEMASK_ITEM|TYPEMASK_PLAYER);
// no or incorrect quest giver
if (!pObject || (pObject->GetTypeId() != TYPEID_PLAYER && !pObject->hasQuest(quest)) ||
(pObject->GetTypeId() == TYPEID_PLAYER && !pObject->ToPlayer()->CanShareQuest(quest)))
{
_player->PlayerTalkClass->SendCloseGossip();
_player->SetDivider(0);
return;
}
// some kind of WPE protection
if (!_player->CanInteractWithQuestGiver(pObject))
return;
Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest);
if (qInfo)
{
// prevent cheating
if (!GetPlayer()->CanTakeQuest(qInfo, true))
{
_player->PlayerTalkClass->SendCloseGossip();
_player->SetDivider(0);
return;
}
if (_player->GetDivider() != 0)
{
Player* player = ObjectAccessor::FindPlayer(_player->GetDivider());
if (player)
{
player->SendPushToPartyResponse(_player, QUEST_PARTY_MSG_ACCEPT_QUEST);
_player->SetDivider(0);
}
}
if (_player->CanAddQuest(qInfo, true))
{
_player->AddQuest(qInfo, pObject);
if (qInfo->HasFlag(QUEST_FLAGS_PARTY_ACCEPT))
{
if (Group* group = _player->GetGroup())
{
for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
{
Player* player = itr->getSource();
if (!player || player == _player) // not self
continue;
if (player->CanTakeQuest(qInfo, true))
{
player->SetDivider(_player->GetGUID());
//need confirmation that any gossip window will close
player->PlayerTalkClass->SendCloseGossip();
_player->SendQuestConfirmAccept(qInfo, player);
}
}
}
}
if (_player->CanCompleteQuest(quest))
_player->CompleteQuest(quest);
switch (pObject->GetTypeId())
{
case TYPEID_UNIT:
sScriptMgr->OnQuestAccept(_player, (pObject->ToCreature()), qInfo);
(pObject->ToCreature())->AI()->sQuestAccept(_player, qInfo);
break;
case TYPEID_ITEM:
case TYPEID_CONTAINER:
{
sScriptMgr->OnQuestAccept(_player, ((Item*)pObject), qInfo);
// destroy not required for quest finish quest starting item
bool destroyItem = true;
for (int i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i)
{
if ((qInfo->RequiredItemId[i] == ((Item*)pObject)->GetEntry()) && (((Item*)pObject)->GetTemplate()->MaxCount > 0))
{
destroyItem = false;
break;
}
}
if (destroyItem)
_player->DestroyItem(((Item*)pObject)->GetBagSlot(), ((Item*)pObject)->GetSlot(), true);
break;
}
case TYPEID_GAMEOBJECT:
sScriptMgr->OnQuestAccept(_player, ((GameObject*)pObject), qInfo);
(pObject->ToGameObject())->AI()->QuestAccept(_player, qInfo);
break;
default:
break;
}
_player->PlayerTalkClass->SendCloseGossip();
if (qInfo->GetSrcSpell() > 0)
_player->CastSpell(_player, qInfo->GetSrcSpell(), true);
return;
}
}
_player->PlayerTalkClass->SendCloseGossip();
}
void WorldSession::HandleQuestgiverQueryQuestOpcode(WorldPacket & recv_data)
{
uint64 guid;
uint32 questId;
uint8 unk1;
recv_data >> guid >> questId >> unk1;
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTGIVER_QUERY_QUEST npc = %u, quest = %u, unk1 = %u", uint32(GUID_LOPART(guid)), questId, unk1);
// Verify that the guid is valid and is a questgiver or involved in the requested quest
Object* object = ObjectAccessor::GetObjectByTypeMask(*_player, guid, TYPEMASK_UNIT | TYPEMASK_GAMEOBJECT | TYPEMASK_ITEM);
if (!object || (!object->hasQuest(questId) && !object->hasInvolvedQuest(questId)))
{
_player->PlayerTalkClass->SendCloseGossip();
return;
}
Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
if (quest)
{
// not sure here what should happen to quests with QUEST_FLAGS_AUTOCOMPLETE
// if this breaks them, add && object->GetTypeId() == TYPEID_ITEM to this check
// item-started quests never have that flag
if (!_player->CanTakeQuest(quest, true))
return;
if (quest->IsAutoAccept() && _player->CanAddQuest(quest, true))
{
_player->AddQuest(quest, object);
if (_player->CanCompleteQuest(questId))
_player->CompleteQuest(questId);
}
if (quest->HasFlag(QUEST_FLAGS_AUTOCOMPLETE))
_player->PlayerTalkClass->SendQuestGiverRequestItems(quest, object->GetGUID(), _player->CanCompleteQuest(quest->GetQuestId()), true);
else
_player->PlayerTalkClass->SendQuestGiverQuestDetails(quest, object->GetGUID(), true);
}
}
void WorldSession::HandleQuestQueryOpcode(WorldPacket & recv_data)
{
if (!_player)
return;
uint32 quest;
recv_data >> quest;
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUEST_QUERY quest = %u", quest);
Quest const* pQuest = sObjectMgr->GetQuestTemplate(quest);
if (pQuest)
{
_player->PlayerTalkClass->SendQuestQueryResponse(pQuest);
}
}
void WorldSession::HandleQuestgiverChooseRewardOpcode(WorldPacket & recv_data)
{
uint32 questId, reward;
uint64 guid;
recv_data >> guid >> questId >> reward;
if (reward >= QUEST_REWARD_CHOICES_COUNT)
{
sLog->outError("Error in CMSG_QUESTGIVER_CHOOSE_REWARD: player %s (guid %d) tried to get invalid reward (%u) (probably packet hacking)", _player->GetName(), _player->GetGUIDLow(), reward);
return;
}
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTGIVER_CHOOSE_REWARD npc = %u, quest = %u, reward = %u", uint32(GUID_LOPART(guid)), questId, reward);
Object* object = ObjectAccessor::GetObjectByTypeMask(*_player, guid, TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT);
if (!object || !object->hasInvolvedQuest(questId))
return;
// some kind of WPE protection
if (!_player->CanInteractWithQuestGiver(object))
return;
if (Quest const* quest = sObjectMgr->GetQuestTemplate(questId))
{
if ((!_player->CanSeeStartQuest(quest) && _player->GetQuestStatus(questId) == QUEST_STATUS_NONE) ||
(_player->GetQuestStatus(questId) != QUEST_STATUS_COMPLETE && !quest->IsAutoComplete()))
{
sLog->outError("HACK ALERT: Player %s (guid: %u) is trying to complete quest (id: %u) but he has no right to do it!",
_player->GetName(), _player->GetGUIDLow(), questId);
return;
}
if (_player->CanRewardQuest(quest, reward, true))
{
_player->RewardQuest(quest, reward, object);
switch (object->GetTypeId())
{
case TYPEID_UNIT:
if (!(sScriptMgr->OnQuestReward(_player, (object->ToCreature()), quest, reward)))
{
// Send next quest
if (Quest const* nextQuest = _player->GetNextQuest(guid, quest))
{
if (nextQuest->IsAutoAccept() && _player->CanAddQuest(nextQuest, true) && _player->CanTakeQuest(quest, true))
{
_player->AddQuest(nextQuest, object);
if (_player->CanCompleteQuest(nextQuest->GetQuestId()))
_player->CompleteQuest(nextQuest->GetQuestId());
}
_player->PlayerTalkClass->SendQuestGiverQuestDetails(nextQuest, guid, true);
}
(object->ToCreature())->AI()->sQuestReward(_player, quest, reward);
}
break;
case TYPEID_GAMEOBJECT:
if (!sScriptMgr->OnQuestReward(_player, ((GameObject*)object), quest, reward))
{
// Send next quest
if (Quest const* nextQuest = _player->GetNextQuest(guid, quest))
{
if (nextQuest->IsAutoAccept() && _player->CanAddQuest(nextQuest, true) && _player->CanTakeQuest(quest, true))
{
_player->AddQuest(nextQuest, object);
if (_player->CanCompleteQuest(nextQuest->GetQuestId()))
_player->CompleteQuest(nextQuest->GetQuestId());
}
_player->PlayerTalkClass->SendQuestGiverQuestDetails(nextQuest, guid, true);
}
object->ToGameObject()->AI()->QuestReward(_player, quest, reward);
}
break;
default:
break;
}
}
else
_player->PlayerTalkClass->SendQuestGiverOfferReward(quest, guid, true);
}
}
void WorldSession::HandleQuestgiverRequestRewardOpcode(WorldPacket & recv_data)
{
uint32 quest;
uint64 guid;
recv_data >> guid >> quest;
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTGIVER_REQUEST_REWARD npc = %u, quest = %u", uint32(GUID_LOPART(guid)), quest);
Object* pObject = ObjectAccessor::GetObjectByTypeMask(*_player, guid, TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT);
if (!pObject || !pObject->hasInvolvedQuest(quest))
return;
// some kind of WPE protection
if (!_player->CanInteractWithQuestGiver(pObject))
return;
if (_player->CanCompleteQuest(quest))
_player->CompleteQuest(quest);
if (_player->GetQuestStatus(quest) != QUEST_STATUS_COMPLETE)
return;
if (Quest const* pQuest = sObjectMgr->GetQuestTemplate(quest))
_player->PlayerTalkClass->SendQuestGiverOfferReward(pQuest, guid, true);
}
void WorldSession::HandleQuestgiverCancel(WorldPacket& /*recv_data*/)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTGIVER_CANCEL");
_player->PlayerTalkClass->SendCloseGossip();
}
void WorldSession::HandleQuestLogSwapQuest(WorldPacket& recv_data)
{
uint8 slot1, slot2;
recv_data >> slot1 >> slot2;
if (slot1 == slot2 || slot1 >= MAX_QUEST_LOG_SIZE || slot2 >= MAX_QUEST_LOG_SIZE)
return;
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTLOG_SWAP_QUEST slot 1 = %u, slot 2 = %u", slot1, slot2);
GetPlayer()->SwapQuestSlot(slot1, slot2);
}
void WorldSession::HandleQuestLogRemoveQuest(WorldPacket& recv_data)
{
uint8 slot;
recv_data >> slot;
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTLOG_REMOVE_QUEST slot = %u", slot);
if (slot < MAX_QUEST_LOG_SIZE)
{
if (uint32 quest = _player->GetQuestSlotQuestId(slot))
{
if (!_player->TakeQuestSourceItem(quest, true))
return; // can't un-equip some items, reject quest cancel
if (const Quest *pQuest = sObjectMgr->GetQuestTemplate(quest))
{
if (pQuest->HasFlag(QUEST_TRINITY_FLAGS_TIMED))
_player->RemoveTimedQuest(quest);
}
_player->TakeQuestSourceItem(quest, true); // remove quest src item from player
_player->RemoveActiveQuest(quest);
_player->GetAchievementMgr().RemoveTimedAchievement(ACHIEVEMENT_TIMED_TYPE_QUEST, quest);
sLog->outDetail("Player %u abandoned quest %u", _player->GetGUIDLow(), quest);
}
_player->SetQuestSlot(slot, 0);
_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED, 1);
}
}
void WorldSession::HandleQuestConfirmAccept(WorldPacket& recv_data)
{
uint32 quest;
recv_data >> quest;
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUEST_CONFIRM_ACCEPT quest = %u", quest);
if (const Quest* pQuest = sObjectMgr->GetQuestTemplate(quest))
{
if (!pQuest->HasFlag(QUEST_FLAGS_PARTY_ACCEPT))
return;
Player* pOriginalPlayer = ObjectAccessor::FindPlayer(_player->GetDivider());
if (!pOriginalPlayer)
return;
if (pQuest->IsRaidQuest())
{
if (!_player->IsInSameRaidWith(pOriginalPlayer))
return;
}
else
{
if (!_player->IsInSameGroupWith(pOriginalPlayer))
return;
}
if (_player->CanAddQuest(pQuest, true))
_player->AddQuest(pQuest, NULL); // NULL, this prevent DB script from duplicate running
_player->SetDivider(0);
}
}
void WorldSession::HandleQuestgiverCompleteQuest(WorldPacket& recv_data)
{
uint32 quest;
uint64 guid;
recv_data >> guid >> quest;
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTGIVER_COMPLETE_QUEST npc = %u, quest = %u", uint32(GUID_LOPART(guid)), quest);
Object* pObject = ObjectAccessor::GetObjectByTypeMask(*_player, guid, TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT);
if (!pObject || !pObject->hasInvolvedQuest(quest))
return;
// some kind of WPE protection
if (!_player->CanInteractWithQuestGiver(pObject))
return;
Quest const* pQuest = sObjectMgr->GetQuestTemplate(quest);
if (pQuest)
{
if (!_player->CanSeeStartQuest(pQuest) && _player->GetQuestStatus(quest)==QUEST_STATUS_NONE)
{
sLog->outError("Possible hacking attempt: Player %s [guid: %u] tried to complete quest [entry: %u] without being in possession of the quest!",
_player->GetName(), _player->GetGUIDLow(), quest);
return;
}
// TODO: need a virtual function
if (_player->InBattleground())
if (Battleground* bg = _player->GetBattleground())
if (bg->GetTypeID() == BATTLEGROUND_AV)
((BattlegroundAV*)bg)->HandleQuestComplete(quest, _player);
if (_player->GetQuestStatus(quest) != QUEST_STATUS_COMPLETE)
{
if (pQuest->IsRepeatable())
_player->PlayerTalkClass->SendQuestGiverRequestItems(pQuest, guid, _player->CanCompleteRepeatableQuest(pQuest), false);
else
_player->PlayerTalkClass->SendQuestGiverRequestItems(pQuest, guid, _player->CanRewardQuest(pQuest, false), false);
}
else
{
if (pQuest->GetReqItemsCount()) // some items required
_player->PlayerTalkClass->SendQuestGiverRequestItems(pQuest, guid, _player->CanRewardQuest(pQuest, false), false);
else // no items required
_player->PlayerTalkClass->SendQuestGiverOfferReward(pQuest, guid, true);
}
}
}
void WorldSession::HandleQuestgiverQuestAutoLaunch(WorldPacket& /*recvPacket*/)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTGIVER_QUEST_AUTOLAUNCH");
}
void WorldSession::HandlePushQuestToParty(WorldPacket& recvPacket)
{
uint32 questId;
recvPacket >> questId;
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_PUSHQUESTTOPARTY quest = %u", questId);
if (Quest const* pQuest = sObjectMgr->GetQuestTemplate(questId))
{
if (Group* group = _player->GetGroup())
{
for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
{
Player* player = itr->getSource();
if (!player || player == _player) // skip self
continue;
_player->SendPushToPartyResponse(player, QUEST_PARTY_MSG_SHARING_QUEST);
if (!player->SatisfyQuestStatus(pQuest, false))
{
_player->SendPushToPartyResponse(player, QUEST_PARTY_MSG_HAVE_QUEST);
continue;
}
if (player->GetQuestStatus(questId) == QUEST_STATUS_COMPLETE)
{
_player->SendPushToPartyResponse(player, QUEST_PARTY_MSG_FINISH_QUEST);
continue;
}
if (!player->CanTakeQuest(pQuest, false))
{
_player->SendPushToPartyResponse(player, QUEST_PARTY_MSG_CANT_TAKE_QUEST);
continue;
}
if (!player->SatisfyQuestLog(false))
{
_player->SendPushToPartyResponse(player, QUEST_PARTY_MSG_LOG_FULL);
continue;
}
if (player->GetDivider() != 0)
{
_player->SendPushToPartyResponse(player, QUEST_PARTY_MSG_BUSY);
continue;
}
player->PlayerTalkClass->SendQuestGiverQuestDetails(pQuest, _player->GetGUID(), true);
player->SetDivider(_player->GetGUID());
}
}
}
}
void WorldSession::HandleQuestPushResult(WorldPacket& recvPacket)
{
uint64 guid;
uint8 msg;
recvPacket >> guid >> msg;
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received MSG_QUEST_PUSH_RESULT");
if (_player->GetDivider() != 0)
{
Player* player = ObjectAccessor::FindPlayer(_player->GetDivider());
if (player)
{
WorldPacket data(MSG_QUEST_PUSH_RESULT, (8+1));
data << uint64(guid);
data << uint8(msg); // valid values: 0-8
player->GetSession()->SendPacket(&data);
_player->SetDivider(0);
}
}
}
uint32 WorldSession::getDialogStatus(Player* player, Object* questgiver, uint32 defstatus)
{
uint32 result = defstatus;
QuestRelationBounds qr;
QuestRelationBounds qir;
switch (questgiver->GetTypeId())
{
case TYPEID_GAMEOBJECT:
{
qr = sObjectMgr->GetGOQuestRelationBounds(questgiver->GetEntry());
qir = sObjectMgr->GetGOQuestInvolvedRelationBounds(questgiver->GetEntry());
break;
}
case TYPEID_UNIT:
{
qr = sObjectMgr->GetCreatureQuestRelationBounds(questgiver->GetEntry());
qir = sObjectMgr->GetCreatureQuestInvolvedRelationBounds(questgiver->GetEntry());
break;
}
default:
//its imposible, but check ^)
sLog->outError("Warning: GetDialogStatus called for unexpected type %u", questgiver->GetTypeId());
return DIALOG_STATUS_NONE;
}
for (QuestRelations::const_iterator i = qir.first; i != qir.second; ++i)
{
uint32 result2 = 0;
uint32 quest_id = i->second;
Quest const* pQuest = sObjectMgr->GetQuestTemplate(quest_id);
if (!pQuest) continue;
ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_QUEST_SHOW_MARK, pQuest->GetQuestId());
if (!sConditionMgr->IsPlayerMeetToConditions(player, conditions))
continue;
QuestStatus status = player->GetQuestStatus(quest_id);
if ((status == QUEST_STATUS_COMPLETE && !player->GetQuestRewardStatus(quest_id)) ||
(pQuest->IsAutoComplete() && player->CanTakeQuest(pQuest, false)))
{
if (pQuest->IsAutoComplete() && pQuest->IsRepeatable())
result2 = DIALOG_STATUS_REWARD_REP;
else
result2 = DIALOG_STATUS_REWARD;
}
else if (status == QUEST_STATUS_INCOMPLETE)
result2 = DIALOG_STATUS_INCOMPLETE;
if (result2 > result)
result = result2;
}
for (QuestRelations::const_iterator i = qr.first; i != qr.second; ++i)
{
uint32 result2 = 0;
uint32 quest_id = i->second;
Quest const* pQuest = sObjectMgr->GetQuestTemplate(quest_id);
if (!pQuest)
continue;
ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_QUEST_SHOW_MARK, pQuest->GetQuestId());
if (!sConditionMgr->IsPlayerMeetToConditions(player, conditions))
continue;
QuestStatus status = player->GetQuestStatus(quest_id);
if (status == QUEST_STATUS_NONE)
{
if (player->CanSeeStartQuest(pQuest))
{
if (player->SatisfyQuestLevel(pQuest, false))
{
if (pQuest->IsAutoComplete() || (pQuest->IsRepeatable() && player->IsQuestRewarded(quest_id)))
result2 = DIALOG_STATUS_REWARD_REP;
else if (player->getLevel() <= ((player->GetQuestLevel(pQuest) == -1) ? player->getLevel() : player->GetQuestLevel(pQuest) + sWorld->getIntConfig(CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF)))
{
if (pQuest->HasFlag(QUEST_FLAGS_DAILY) || pQuest->HasFlag(QUEST_FLAGS_WEEKLY))
result2 = DIALOG_STATUS_AVAILABLE_REP;
else
result2 = DIALOG_STATUS_AVAILABLE;
}
else
result2 = DIALOG_STATUS_LOW_LEVEL_AVAILABLE;
}
else
result2 = DIALOG_STATUS_UNAVAILABLE;
}
}
if (result2 > result)
result = result2;
}
return result;
}
void WorldSession::HandleQuestgiverStatusMultipleQuery(WorldPacket& /*recvPacket*/)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY");
uint32 count = 0;
WorldPacket data(SMSG_QUESTGIVER_STATUS_MULTIPLE, 4);
data << uint32(count); // placeholder
for (Player::ClientGUIDs::const_iterator itr = _player->m_clientGUIDs.begin(); itr != _player->m_clientGUIDs.end(); ++itr)
{
uint8 questStatus = DIALOG_STATUS_NONE;
uint8 defstatus = DIALOG_STATUS_NONE;
if (IS_CRE_OR_VEH_OR_PET_GUID(*itr))
{
// need also pet quests case support
Creature* questgiver = ObjectAccessor::GetCreatureOrPetOrVehicle(*GetPlayer(), *itr);
if (!questgiver || questgiver->IsHostileTo(_player))
continue;
if (!questgiver->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER))
continue;
questStatus = sScriptMgr->GetDialogStatus(_player, questgiver);
if (questStatus > 6)
questStatus = getDialogStatus(_player, questgiver, defstatus);
data << uint64(questgiver->GetGUID());
data << uint8(questStatus);
++count;
}
else if (IS_GAMEOBJECT_GUID(*itr))
{
GameObject* questgiver = GetPlayer()->GetMap()->GetGameObject(*itr);
if (!questgiver)
continue;
if (questgiver->GetGoType() != GAMEOBJECT_TYPE_QUESTGIVER)
continue;
questStatus = sScriptMgr->GetDialogStatus(_player, questgiver);
if (questStatus > 6)
questStatus = getDialogStatus(_player, questgiver, defstatus);
data << uint64(questgiver->GetGUID());
data << uint8(questStatus);
++count;
}
}
data.put<uint32>(0, count); // write real count
SendPacket(&data);
}
void WorldSession::HandleQueryQuestsCompleted(WorldPacket & /*recv_data*/)
{
size_t rew_count = _player->GetRewardedQuestCount();
WorldPacket data(SMSG_QUERY_QUESTS_COMPLETED_RESPONSE, 4 + 4 * rew_count);
data << uint32(rew_count);
const RewardedQuestSet &rewQuests = _player->getRewardedQuests();
for (RewardedQuestSet::const_iterator itr = rewQuests.begin(); itr != rewQuests.end(); ++itr)
data << uint32(*itr);
SendPacket(&data);
}
@@ -0,0 +1,86 @@
/*
* Copyright (C) 2011 TrinityCore <http://www.trinitycore.org/>
*
* 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 "WorldSession.h"
#include "Player.h"
#include "ObjectMgr.h"
#include "Opcodes.h"
#include "Log.h"
void WorldSession::HandleGrantLevel(WorldPacket& recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_GRANT_LEVEL");
uint64 guid;
recv_data.readPackGUID(guid);
Player* target = ObjectAccessor::GetObjectInWorld(guid, _player);
// check cheating
uint8 levels = _player->GetGrantableLevels();
uint8 error = 0;
if (!target)
error = ERR_REFER_A_FRIEND_NO_TARGET;
else if (levels == 0)
error = ERR_REFER_A_FRIEND_INSUFFICIENT_GRANTABLE_LEVELS;
else if (GetRecruiterId() != target->GetSession()->GetAccountId())
error = ERR_REFER_A_FRIEND_NOT_REFERRED_BY;
else if (target->GetTeamId() != _player->GetTeamId())
error = ERR_REFER_A_FRIEND_DIFFERENT_FACTION;
else if (target->getLevel() >= _player->getLevel())
error = ERR_REFER_A_FRIEND_TARGET_TOO_HIGH;
else if (target->getLevel() >= sWorld->getIntConfig(CONFIG_MAX_RECRUIT_A_FRIEND_BONUS_PLAYER_LEVEL))
error = ERR_REFER_A_FRIEND_GRANT_LEVEL_MAX_I;
else if (target->GetGroup() != _player->GetGroup())
error = ERR_REFER_A_FRIEND_NOT_IN_GROUP;
if (error) {
WorldPacket data(SMSG_REFER_A_FRIEND_FAILURE, 24);
data << uint32(error);
if (error == ERR_REFER_A_FRIEND_NOT_IN_GROUP)
data << target->GetName();
SendPacket(&data);
return;
}
WorldPacket data2(SMSG_PROPOSE_LEVEL_GRANT, 8);
data2.append(_player->GetPackGUID());
target->GetSession()->SendPacket(&data2);
}
void WorldSession::HandleAcceptGrantLevel(WorldPacket& recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_ACCEPT_LEVEL_GRANT");
uint64 guid;
recv_data.readPackGUID(guid);
Player* other = ObjectAccessor::GetObjectInWorld(guid, _player);
if (!(other && other->GetSession()))
return;
if (GetAccountId() != other->GetSession()->GetRecruiterId())
return;
if (other->GetGrantableLevels())
other->SetGrantableLevels(other->GetGrantableLevels() - 1);
else
return;
_player->GiveLevel(_player->getLevel() + 1);
}
+93
View File
@@ -0,0 +1,93 @@
/*
* Copyright (C) 2008-2012 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 "DatabaseEnv.h"
#include "Opcodes.h"
#include "Log.h"
#include "Player.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "ObjectAccessor.h"
#include "UpdateMask.h"
void WorldSession::HandleLearnTalentOpcode(WorldPacket & recv_data)
{
uint32 talent_id, requested_rank;
recv_data >> talent_id >> requested_rank;
_player->LearnTalent(talent_id, requested_rank);
_player->SendTalentsInfoData(false);
}
void WorldSession::HandleLearnPreviewTalents(WorldPacket& recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LEARN_PREVIEW_TALENTS");
uint32 talentsCount;
recvPacket >> talentsCount;
uint32 talentId, talentRank;
for (uint32 i = 0; i < talentsCount; ++i)
{
recvPacket >> talentId >> talentRank;
_player->LearnTalent(talentId, talentRank);
}
_player->SendTalentsInfoData(false);
}
void WorldSession::HandleTalentWipeConfirmOpcode(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "MSG_TALENT_WIPE_CONFIRM");
uint64 guid;
recv_data >> guid;
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_TRAINER);
if (!unit)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleTalentWipeConfirmOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)));
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
if (!(_player->resetTalents()))
{
WorldPacket data(MSG_TALENT_WIPE_CONFIRM, 8+4); //you have not any talent
data << uint64(0);
data << uint32(0);
SendPacket(&data);
return;
}
_player->SendTalentsInfoData(false);
unit->CastSpell(_player, 14867, true); //spell: "Untalent Visual Effect"
}
void WorldSession::HandleUnlearnSkillOpcode(WorldPacket & recv_data)
{
uint32 skill_id;
recv_data >> skill_id;
GetPlayer()->SetSkill(skill_id, 0, 0, 0);
}
+686
View File
@@ -0,0 +1,686 @@
/*
* Copyright (C) 2008-2012 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 "DBCStores.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "ObjectMgr.h"
#include "SpellMgr.h"
#include "Log.h"
#include "Opcodes.h"
#include "Spell.h"
#include "Totem.h"
#include "TemporarySummon.h"
#include "SpellAuras.h"
#include "CreatureAI.h"
#include "ScriptMgr.h"
#include "GameObjectAI.h"
#include "SpellAuraEffects.h"
void WorldSession::HandleClientCastFlags(WorldPacket& recvPacket, uint8 castFlags, SpellCastTargets& targets)
{
// some spell cast packet including more data (for projectiles?)
if (castFlags & 0x02)
{
// not sure about these two
float elevation, speed;
recvPacket >> elevation;
recvPacket >> speed;
targets.SetElevation(elevation);
targets.SetSpeed(speed);
uint8 hasMovementData;
recvPacket >> hasMovementData;
if (hasMovementData)
{
recvPacket.rfinish();
// movement packet for caster of the spell
/*recvPacket.read_skip<uint32>(); // MSG_MOVE_STOP - hardcoded in client
uint64 guid;
recvPacket.readPackGUID(guid);
MovementInfo movementInfo;
movementInfo.guid = guid;
ReadMovementInfo(recvPacket, &movementInfo);*/
}
}
}
void WorldSession::HandleUseItemOpcode(WorldPacket& recvPacket)
{
// TODO: add targets.read() check
Player* pUser = _player;
// ignore for remote control state
if (pUser->m_mover != pUser)
return;
uint8 bagIndex, slot, castFlags;
uint8 castCount; // next cast if exists (single or not)
uint64 itemGUID;
uint32 glyphIndex; // something to do with glyphs?
uint32 spellId; // casted spell id
recvPacket >> bagIndex >> slot >> castCount >> spellId >> itemGUID >> glyphIndex >> castFlags;
if (glyphIndex >= MAX_GLYPH_SLOT_INDEX)
{
pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL);
return;
}
Item* pItem = pUser->GetUseableItemByPos(bagIndex, slot);
if (!pItem)
{
pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL);
return;
}
if (pItem->GetGUID() != itemGUID)
{
pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL);
return;
}
sLog->outDetail("WORLD: CMSG_USE_ITEM packet, bagIndex: %u, slot: %u, castCount: %u, spellId: %u, Item: %u, glyphIndex: %u, data length = %i", bagIndex, slot, castCount, spellId, pItem->GetEntry(), glyphIndex, (uint32)recvPacket.size());
ItemTemplate const* proto = pItem->GetTemplate();
if (!proto)
{
pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL);
return;
}
// some item classes can be used only in equipped state
if (proto->InventoryType != INVTYPE_NON_EQUIP && !pItem->IsEquipped())
{
pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL);
return;
}
InventoryResult msg = pUser->CanUseItem(pItem);
if (msg != EQUIP_ERR_OK)
{
pUser->SendEquipError(msg, pItem, NULL);
return;
}
// only allow conjured consumable, bandage, poisons (all should have the 2^21 item flag set in DB)
if (proto->Class == ITEM_CLASS_CONSUMABLE && !(proto->Flags & ITEM_PROTO_FLAG_USEABLE_IN_ARENA) && pUser->InArena())
{
pUser->SendEquipError(EQUIP_ERR_NOT_DURING_ARENA_MATCH, pItem, NULL);
return;
}
// don't allow items banned in arena
if (proto->Flags & ITEM_PROTO_FLAG_NOT_USEABLE_IN_ARENA && pUser->InArena())
{
pUser->SendEquipError(EQUIP_ERR_NOT_DURING_ARENA_MATCH, pItem, NULL);
return;
}
if (pUser->isInCombat())
{
for (int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
{
if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(proto->Spells[i].SpellId))
{
if (!spellInfo->CanBeUsedInCombat())
{
pUser->SendEquipError(EQUIP_ERR_NOT_IN_COMBAT, pItem, NULL);
return;
}
}
}
}
// check also BIND_WHEN_PICKED_UP and BIND_QUEST_ITEM for .additem or .additemset case by GM (not binded at adding to inventory)
if (pItem->GetTemplate()->Bonding == BIND_WHEN_USE || pItem->GetTemplate()->Bonding == BIND_WHEN_PICKED_UP || pItem->GetTemplate()->Bonding == BIND_QUEST_ITEM)
{
if (!pItem->IsSoulBound())
{
pItem->SetState(ITEM_CHANGED, pUser);
pItem->SetBinding(true);
}
}
SpellCastTargets targets;
targets.Read(recvPacket, pUser);
HandleClientCastFlags(recvPacket, castFlags, targets);
if (!pItem->IsTargetValidForItemUse(targets.GetUnitTarget()))
{
// free gray item after use fail
pUser->SendEquipError(EQUIP_ERR_NONE, pItem, NULL);
// send spell error
if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId))
{
// for implicit area/coord target spells
if (!targets.GetUnitTarget())
Spell::SendCastResult(_player, spellInfo, castCount, SPELL_FAILED_NO_VALID_TARGETS);
// for explicit target spells
else
Spell::SendCastResult(_player, spellInfo, castCount, SPELL_FAILED_BAD_TARGETS);
}
return;
}
// Note: If script stop casting it must send appropriate data to client to prevent stuck item in gray state.
if (!sScriptMgr->OnItemUse(pUser, pItem, targets))
{
// no script or script not process request by self
pUser->CastItemUseSpell(pItem, targets, castCount, glyphIndex);
}
}
#define OPEN_CHEST 11437
#define OPEN_SAFE 11535
#define OPEN_CAGE 11792
#define OPEN_BOOTY_CHEST 5107
#define OPEN_STRONGBOX 8517
void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket)
{
sLog->outDetail("WORLD: CMSG_OPEN_ITEM packet, data length = %i", (uint32)recvPacket.size());
Player* pUser = _player;
// ignore for remote control state
if (pUser->m_mover != pUser)
return;
uint8 bagIndex, slot;
recvPacket >> bagIndex >> slot;
sLog->outDetail("bagIndex: %u, slot: %u", bagIndex, slot);
Item* item = pUser->GetItemByPos(bagIndex, slot);
if (!item)
{
pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL);
return;
}
ItemTemplate const* proto = item->GetTemplate();
if (!proto)
{
pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, item, NULL);
return;
}
// Verify that the bag is an actual bag or wrapped item that can be used "normally"
if (!(proto->Flags & ITEM_PROTO_FLAG_OPENABLE) && !item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED))
{
pUser->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW, item, NULL);
sLog->outError("Possible hacking attempt: Player %s [guid: %u] tried to open item [guid: %u, entry: %u] which is not openable!",
pUser->GetName(), pUser->GetGUIDLow(), item->GetGUIDLow(), proto->ItemId);
return;
}
// locked item
uint32 lockId = proto->LockID;
if (lockId)
{
LockEntry const* lockInfo = sLockStore.LookupEntry(lockId);
if (!lockInfo)
{
pUser->SendEquipError(EQUIP_ERR_ITEM_LOCKED, item, NULL);
sLog->outError("WORLD::OpenItem: item [guid = %u] has an unknown lockId: %u!", item->GetGUIDLow(), lockId);
return;
}
// was not unlocked yet
if (item->IsLocked())
{
pUser->SendEquipError(EQUIP_ERR_ITEM_LOCKED, item, NULL);
return;
}
}
if (item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED))// wrapped?
{
QueryResult result = CharacterDatabase.PQuery("SELECT entry, flags FROM character_gifts WHERE item_guid = '%u'", item->GetGUIDLow());
if (result)
{
Field* fields = result->Fetch();
uint32 entry = fields[0].GetUInt32();
uint32 flags = fields[1].GetUInt32();
item->SetUInt64Value(ITEM_FIELD_GIFTCREATOR, 0);
item->SetEntry(entry);
item->SetUInt32Value(ITEM_FIELD_FLAGS, flags);
item->SetState(ITEM_CHANGED, pUser);
}
else
{
sLog->outError("Wrapped item %u don't have record in character_gifts table and will deleted", item->GetGUIDLow());
pUser->DestroyItem(item->GetBagSlot(), item->GetSlot(), true);
return;
}
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GIFT);
stmt->setUInt32(0, item->GetGUIDLow());
CharacterDatabase.Execute(stmt);
}
else
pUser->SendLoot(item->GetGUID(), LOOT_CORPSE);
}
void WorldSession::HandleGameObjectUseOpcode(WorldPacket & recv_data)
{
uint64 guid;
recv_data >> guid;
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_GAMEOBJ_USE Message [guid=%u]", GUID_LOPART(guid));
// ignore for remote control state
if (_player->m_mover != _player)
return;
if (GameObject* obj = GetPlayer()->GetMap()->GetGameObject(guid))
obj->Use(_player);
}
void WorldSession::HandleGameobjectReportUse(WorldPacket& recvPacket)
{
uint64 guid;
recvPacket >> guid;
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_GAMEOBJ_REPORT_USE Message [in game guid: %u]", GUID_LOPART(guid));
// ignore for remote control state
if (_player->m_mover != _player)
return;
GameObject* go = GetPlayer()->GetMap()->GetGameObject(guid);
if (!go)
return;
if (!go->IsWithinDistInMap(_player, INTERACTION_DISTANCE))
return;
go->AI()->GossipHello(_player);
_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT, go->GetEntry());
}
void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket)
{
uint32 spellId;
uint8 castCount, castFlags;
recvPacket >> castCount >> spellId >> castFlags;
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: got cast spell packet, castCount: %u, spellId: %u, castFlags: %u, data length = %u", castCount, spellId, castFlags, (uint32)recvPacket.size());
// ignore for remote control state (for player case)
Unit* mover = _player->m_mover;
if (mover != _player && mover->GetTypeId() == TYPEID_PLAYER)
{
recvPacket.rfinish(); // prevent spam at ignore packet
return;
}
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
if (!spellInfo)
{
sLog->outError("WORLD: unknown spell id %u", spellId);
recvPacket.rfinish(); // prevent spam at ignore packet
return;
}
if (mover->GetTypeId() == TYPEID_PLAYER)
{
// not have spell in spellbook or spell passive and not casted by client
if (!mover->ToPlayer()->HasActiveSpell (spellId) || spellInfo->IsPassive())
{
//cheater? kick? ban?
recvPacket.rfinish(); // prevent spam at ignore packet
return;
}
}
else
{
// not have spell in spellbook or spell passive and not casted by client
if ((mover->GetTypeId() == TYPEID_UNIT && !mover->ToCreature()->HasSpell(spellId)) || spellInfo->IsPassive())
{
//cheater? kick? ban?
recvPacket.rfinish(); // prevent spam at ignore packet
return;
}
}
// Client is resending autoshot cast opcode when other spell is casted during shoot rotation
// Skip it to prevent "interrupt" message
if (spellInfo->IsAutoRepeatRangedSpell() && _player->GetCurrentSpell(CURRENT_AUTOREPEAT_SPELL)
&& _player->GetCurrentSpell(CURRENT_AUTOREPEAT_SPELL)->m_spellInfo == spellInfo)
{
recvPacket.rfinish();
return;
}
// can't use our own spells when we're in possession of another unit,
if (_player->isPossessing())
{
recvPacket.rfinish(); // prevent spam at ignore packet
return;
}
// client provided targets
SpellCastTargets targets;
targets.Read(recvPacket, mover);
HandleClientCastFlags(recvPacket, castFlags, targets);
// auto-selection buff level base at target level (in spellInfo)
if (targets.GetUnitTarget())
{
SpellInfo const* actualSpellInfo = spellInfo->GetAuraRankForLevel(targets.GetUnitTarget()->getLevel());
// if rank not found then function return NULL but in explicit cast case original spell can be casted and later failed with appropriate error message
if (actualSpellInfo)
spellInfo = actualSpellInfo;
}
Spell* spell = new Spell(mover, spellInfo, TRIGGERED_NONE, 0, false);
spell->m_cast_count = castCount; // set count of casts
spell->prepare(&targets);
}
void WorldSession::HandleCancelCastOpcode(WorldPacket& recvPacket)
{
uint32 spellId;
recvPacket.read_skip<uint8>(); // counter, increments with every CANCEL packet, don't use for now
recvPacket >> spellId;
if (_player->IsNonMeleeSpellCasted(false))
_player->InterruptNonMeleeSpells(false, spellId, false);
}
void WorldSession::HandleCancelAuraOpcode(WorldPacket& recvPacket)
{
uint32 spellId;
recvPacket >> spellId;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
if (!spellInfo)
return;
// not allow remove spells with attr SPELL_ATTR0_CANT_CANCEL
if (spellInfo->Attributes & SPELL_ATTR0_CANT_CANCEL)
return;
// channeled spell case (it currently casted then)
if (spellInfo->IsChanneled())
{
if (Spell* curSpell = _player->GetCurrentSpell(CURRENT_CHANNELED_SPELL))
if (curSpell->m_spellInfo->Id == spellId)
_player->InterruptSpell(CURRENT_CHANNELED_SPELL);
return;
}
// non channeled case:
// don't allow remove non positive spells
// don't allow cancelling passive auras (some of them are visible)
if (!spellInfo->IsPositive() || spellInfo->IsPassive())
return;
// maybe should only remove one buff when there are multiple?
_player->RemoveOwnedAura(spellId, 0, 0, AURA_REMOVE_BY_CANCEL);
}
void WorldSession::HandlePetCancelAuraOpcode(WorldPacket& recvPacket)
{
uint64 guid;
uint32 spellId;
recvPacket >> guid;
recvPacket >> spellId;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
if (!spellInfo)
{
sLog->outError("WORLD: unknown PET spell id %u", spellId);
return;
}
Creature* pet=ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid);
if (!pet)
{
sLog->outError("HandlePetCancelAura: Attempt to cancel an aura for non-existant pet %u by player '%s'", uint32(GUID_LOPART(guid)), GetPlayer()->GetName());
return;
}
if (pet != GetPlayer()->GetGuardianPet() && pet != GetPlayer()->GetCharm())
{
sLog->outError("HandlePetCancelAura: Pet %u is not a pet of player '%s'", uint32(GUID_LOPART(guid)), GetPlayer()->GetName());
return;
}
if (!pet->isAlive())
{
pet->SendPetActionFeedback(FEEDBACK_PET_DEAD);
return;
}
pet->RemoveOwnedAura(spellId, 0, 0, AURA_REMOVE_BY_CANCEL);
pet->AddCreatureSpellCooldown(spellId);
}
void WorldSession::HandleCancelGrowthAuraOpcode(WorldPacket& /*recvPacket*/)
{
}
void WorldSession::HandleCancelAutoRepeatSpellOpcode(WorldPacket& /*recvPacket*/)
{
// may be better send SMSG_CANCEL_AUTO_REPEAT?
// cancel and prepare for deleting
_player->InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
}
void WorldSession::HandleCancelChanneling(WorldPacket & recv_data)
{
recv_data.read_skip<uint32>(); // spellid, not used
// ignore for remote control state (for player case)
Unit* mover = _player->m_mover;
if (mover != _player && mover->GetTypeId() == TYPEID_PLAYER)
return;
mover->InterruptSpell(CURRENT_CHANNELED_SPELL);
}
void WorldSession::HandleTotemDestroyed(WorldPacket& recvPacket)
{
// ignore for remote control state
if (_player->m_mover != _player)
return;
uint8 slotId;
recvPacket >> slotId;
++slotId;
if (slotId >= MAX_TOTEM_SLOT)
return;
if (!_player->m_SummonSlot[slotId])
return;
Creature* totem = GetPlayer()->GetMap()->GetCreature(_player->m_SummonSlot[slotId]);
// Don't unsummon sentry totem
if (totem && totem->isTotem() && totem->GetEntry() != SENTRY_TOTEM_ENTRY)
totem->ToTotem()->UnSummon();
}
void WorldSession::HandleSelfResOpcode(WorldPacket & /*recv_data*/)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_SELF_RES"); // empty opcode
if (_player->HasAuraType(SPELL_AURA_PREVENT_RESURRECTION))
return; // silent return, client should display error by itself and not send this opcode
if (_player->GetUInt32Value(PLAYER_SELF_RES_SPELL))
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(_player->GetUInt32Value(PLAYER_SELF_RES_SPELL));
if (spellInfo)
_player->CastSpell(_player, spellInfo, false, 0);
_player->SetUInt32Value(PLAYER_SELF_RES_SPELL, 0);
}
}
void WorldSession::HandleSpellClick(WorldPacket& recv_data)
{
uint64 guid;
recv_data >> guid;
// this will get something not in world. crash
Creature* unit = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid);
if (!unit)
return;
// TODO: Unit::SetCharmedBy: 28782 is not in world but 0 is trying to charm it! -> crash
if (!unit->IsInWorld())
return;
unit->HandleSpellClick(_player);
}
void WorldSession::HandleMirrorImageDataRequest(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_GET_MIRRORIMAGE_DATA");
uint64 guid;
recv_data >> guid;
// Get unit for which data is needed by client
Unit* unit = ObjectAccessor::GetObjectInWorld(guid, (Unit*)NULL);
if (!unit)
return;
if (!unit->HasAuraType(SPELL_AURA_CLONE_CASTER))
return;
// Get creator of the unit (SPELL_AURA_CLONE_CASTER does not stack)
Unit* creator = unit->GetAuraEffectsByType(SPELL_AURA_CLONE_CASTER).front()->GetCaster();
if (!creator)
return;
WorldPacket data(SMSG_MIRRORIMAGE_DATA, 68);
data << uint64(guid);
data << uint32(creator->GetDisplayId());
data << uint8(creator->getRace());
data << uint8(creator->getGender());
data << uint8(creator->getClass());
if (creator->GetTypeId() == TYPEID_PLAYER)
{
Player* player = creator->ToPlayer();
data << uint8(player->GetByteValue(PLAYER_BYTES, 0)); // skin
data << uint8(player->GetByteValue(PLAYER_BYTES, 1)); // face
data << uint8(player->GetByteValue(PLAYER_BYTES, 2)); // hair
data << uint8(player->GetByteValue(PLAYER_BYTES, 3)); // haircolor
data << uint8(player->GetByteValue(PLAYER_BYTES_2, 0)); // facialhair
data << uint32(player->GetGuildId()); // unk
static EquipmentSlots const itemSlots[] =
{
EQUIPMENT_SLOT_HEAD,
EQUIPMENT_SLOT_SHOULDERS,
EQUIPMENT_SLOT_BODY,
EQUIPMENT_SLOT_CHEST,
EQUIPMENT_SLOT_WAIST,
EQUIPMENT_SLOT_LEGS,
EQUIPMENT_SLOT_FEET,
EQUIPMENT_SLOT_WRISTS,
EQUIPMENT_SLOT_HANDS,
EQUIPMENT_SLOT_BACK,
EQUIPMENT_SLOT_TABARD,
EQUIPMENT_SLOT_END
};
// Display items in visible slots
for (EquipmentSlots const* itr = &itemSlots[0]; *itr != EQUIPMENT_SLOT_END; ++itr)
{
if (*itr == EQUIPMENT_SLOT_HEAD && player->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM))
data << uint32(0);
else if (*itr == EQUIPMENT_SLOT_BACK && player->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK))
data << uint32(0);
else if (Item const* item = player->GetItemByPos(INVENTORY_SLOT_BAG_0, *itr))
data << uint32(item->GetTemplate()->DisplayInfoID);
else
data << uint32(0);
}
}
else
{
// Skip player data for creatures
data << uint8(0);
data << uint32(0);
data << uint32(0);
data << uint32(0);
data << uint32(0);
data << uint32(0);
data << uint32(0);
data << uint32(0);
data << uint32(0);
data << uint32(0);
data << uint32(0);
data << uint32(0);
data << uint32(0);
data << uint32(0);
}
SendPacket(&data);
}
void WorldSession::HandleUpdateProjectilePosition(WorldPacket& recvPacket)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_UPDATE_PROJECTILE_POSITION");
uint64 casterGuid;
uint32 spellId;
uint8 castCount;
float x, y, z; // Position of missile hit
recvPacket.readPackGUID(casterGuid);
recvPacket >> spellId;
recvPacket >> castCount;
recvPacket >> x;
recvPacket >> y;
recvPacket >> z;
WorldPacket data(SMSG_SET_PROJECTILE_POSITION, 21);
data << uint64(casterGuid);
data << uint8(castCount);
data << float(x);
data << float(y);
data << float(z);
SendPacket(&data);
}
+294
View File
@@ -0,0 +1,294 @@
/*
* Copyright (C) 2008-2012 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 "DatabaseEnv.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "Opcodes.h"
#include "Log.h"
#include "ObjectMgr.h"
#include "Player.h"
#include "UpdateMask.h"
#include "Path.h"
#include "WaypointMovementGenerator.h"
void WorldSession::HandleTaxiNodeStatusQueryOpcode(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_TAXINODE_STATUS_QUERY");
uint64 guid;
recv_data >> guid;
SendTaxiStatus(guid);
}
void WorldSession::SendTaxiStatus(uint64 guid)
{
// cheating checks
Creature* unit = GetPlayer()->GetMap()->GetCreature(guid);
if (!unit)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WorldSession::SendTaxiStatus - Unit (GUID: %u) not found.", uint32(GUID_LOPART(guid)));
return;
}
uint32 curloc = sObjectMgr->GetNearestTaxiNode(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ(), unit->GetMapId(), GetPlayer()->GetTeam());
// not found nearest
if (curloc == 0)
return;
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: current location %u ", curloc);
WorldPacket data(SMSG_TAXINODE_STATUS, 9);
data << guid;
data << uint8(GetPlayer()->m_taxi.IsTaximaskNodeKnown(curloc) ? 1 : 0);
SendPacket(&data);
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_TAXINODE_STATUS");
}
void WorldSession::HandleTaxiQueryAvailableNodes(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_TAXIQUERYAVAILABLENODES");
uint64 guid;
recv_data >> guid;
// cheating checks
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_FLIGHTMASTER);
if (!unit)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleTaxiQueryAvailableNodes - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)));
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
// unknown taxi node case
if (SendLearnNewTaxiNode(unit))
return;
// known taxi node case
SendTaxiMenu(unit);
}
void WorldSession::SendTaxiMenu(Creature* unit)
{
// find current node
uint32 curloc = sObjectMgr->GetNearestTaxiNode(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ(), unit->GetMapId(), GetPlayer()->GetTeam());
if (curloc == 0)
return;
bool lastTaxiCheaterState = GetPlayer()->isTaxiCheater();
if (unit->GetEntry() == 29480) GetPlayer()->SetTaxiCheater(true); // Grimwing in Ebon Hold, special case. NOTE: Not perfect, Zul'Aman should not be included according to WoWhead, and I think taxicheat includes it.
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_TAXINODE_STATUS_QUERY %u ", curloc);
WorldPacket data(SMSG_SHOWTAXINODES, (4+8+4+8*4));
data << uint32(1);
data << uint64(unit->GetGUID());
data << uint32(curloc);
GetPlayer()->m_taxi.AppendTaximaskTo(data, GetPlayer()->isTaxiCheater());
SendPacket(&data);
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_SHOWTAXINODES");
GetPlayer()->SetTaxiCheater(lastTaxiCheaterState);
}
void WorldSession::SendDoFlight(uint32 mountDisplayId, uint32 path, uint32 pathNode)
{
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
while (GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE)
GetPlayer()->GetMotionMaster()->MovementExpired(false);
if (mountDisplayId)
GetPlayer()->Mount(mountDisplayId);
GetPlayer()->GetMotionMaster()->MoveTaxiFlight(path, pathNode);
}
bool WorldSession::SendLearnNewTaxiNode(Creature* unit)
{
// find current node
uint32 curloc = sObjectMgr->GetNearestTaxiNode(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ(), unit->GetMapId(), GetPlayer()->GetTeam());
if (curloc == 0)
return true; // `true` send to avoid WorldSession::SendTaxiMenu call with one more curlock seartch with same false result.
if (GetPlayer()->m_taxi.SetTaximaskNode(curloc))
{
WorldPacket msg(SMSG_NEW_TAXI_PATH, 0);
SendPacket(&msg);
WorldPacket update(SMSG_TAXINODE_STATUS, 9);
update << uint64(unit->GetGUID());
update << uint8(1);
SendPacket(&update);
return true;
}
else
return false;
}
void WorldSession::SendDiscoverNewTaxiNode(uint32 nodeid)
{
if (GetPlayer()->m_taxi.SetTaximaskNode(nodeid))
{
WorldPacket msg(SMSG_NEW_TAXI_PATH, 0);
SendPacket(&msg);
}
}
void WorldSession::HandleActivateTaxiExpressOpcode (WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_ACTIVATETAXIEXPRESS");
uint64 guid;
uint32 node_count;
recv_data >> guid >> node_count;
Creature* npc = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_FLIGHTMASTER);
if (!npc)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleActivateTaxiExpressOpcode - Unit (GUID: %u) not found or you can't interact with it.", uint32(GUID_LOPART(guid)));
return;
}
std::vector<uint32> nodes;
for (uint32 i = 0; i < node_count; ++i)
{
uint32 node;
recv_data >> node;
nodes.push_back(node);
}
if (nodes.empty())
return;
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_ACTIVATETAXIEXPRESS from %d to %d", nodes.front(), nodes.back());
GetPlayer()->ActivateTaxiPathTo(nodes, npc);
}
void WorldSession::HandleMoveSplineDoneOpcode(WorldPacket& recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_MOVE_SPLINE_DONE");
uint64 guid; // used only for proper packet read
recv_data.readPackGUID(guid);
MovementInfo movementInfo; // used only for proper packet read
ReadMovementInfo(recv_data, &movementInfo);
recv_data.read_skip<uint32>(); // unk
// in taxi flight packet received in 2 case:
// 1) end taxi path in far (multi-node) flight
// 2) switch from one map to other in case multim-map taxi path
// we need process only (1)
uint32 curDest = GetPlayer()->m_taxi.GetTaxiDestination();
if (!curDest)
return;
TaxiNodesEntry const* curDestNode = sTaxiNodesStore.LookupEntry(curDest);
// far teleport case
if (curDestNode && curDestNode->map_id != GetPlayer()->GetMapId())
{
if (GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE)
{
// short preparations to continue flight
FlightPathMovementGenerator* flight = (FlightPathMovementGenerator*)(GetPlayer()->GetMotionMaster()->top());
flight->SetCurrentNodeAfterTeleport();
TaxiPathNodeEntry const& node = flight->GetPath()[flight->GetCurrentNode()];
flight->SkipCurrentNode();
GetPlayer()->TeleportTo(curDestNode->map_id, node.x, node.y, node.z, GetPlayer()->GetOrientation());
}
return;
}
uint32 destinationnode = GetPlayer()->m_taxi.NextTaxiDestination();
if (destinationnode > 0) // if more destinations to go
{
// current source node for next destination
uint32 sourcenode = GetPlayer()->m_taxi.GetTaxiSource();
// Add to taximask middle hubs in taxicheat mode (to prevent having player with disabled taxicheat and not having back flight path)
if (GetPlayer()->isTaxiCheater())
{
if (GetPlayer()->m_taxi.SetTaximaskNode(sourcenode))
{
WorldPacket data(SMSG_NEW_TAXI_PATH, 0);
_player->GetSession()->SendPacket(&data);
}
}
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Taxi has to go from %u to %u", sourcenode, destinationnode);
uint32 mountDisplayId = sObjectMgr->GetTaxiMountDisplayId(sourcenode, GetPlayer()->GetTeam());
uint32 path, cost;
sObjectMgr->GetTaxiPath(sourcenode, destinationnode, path, cost);
if (path && mountDisplayId)
SendDoFlight(mountDisplayId, path, 1); // skip start fly node
else
GetPlayer()->m_taxi.ClearTaxiDestinations(); // clear problematic path and next
return;
}
else
GetPlayer()->m_taxi.ClearTaxiDestinations(); // not destinations, clear source node
GetPlayer()->CleanupAfterTaxiFlight();
GetPlayer()->SetFallInformation(0, GetPlayer()->GetPositionZ());
if (GetPlayer()->pvpInfo.inHostileArea)
GetPlayer()->CastSpell(GetPlayer(), 2479, true);
}
void WorldSession::HandleActivateTaxiOpcode(WorldPacket & recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_ACTIVATETAXI");
uint64 guid;
std::vector<uint32> nodes;
nodes.resize(2);
recv_data >> guid >> nodes[0] >> nodes[1];
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_ACTIVATETAXI from %d to %d", nodes[0], nodes[1]);
Creature* npc = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_FLIGHTMASTER);
if (!npc)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleActivateTaxiOpcode - Unit (GUID: %u) not found or you can't interact with it.", uint32(GUID_LOPART(guid)));
return;
}
GetPlayer()->ActivateTaxiPathTo(nodes, npc);
}
+202
View File
@@ -0,0 +1,202 @@
/*
* Copyright (C) 2008-2012 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 "Language.h"
#include "WorldPacket.h"
#include "Common.h"
#include "ObjectMgr.h"
#include "TicketMgr.h"
#include "Player.h"
#include "World.h"
#include "WorldSession.h"
#include "Util.h"
void WorldSession::HandleGMTicketCreateOpcode(WorldPacket & recv_data)
{
// Don't accept tickets if the ticket queue is disabled. (Ticket UI is greyed out but not fully dependable)
if (sTicketMgr->GetStatus() == GMTICKET_QUEUE_STATUS_DISABLED)
return;
if (GetPlayer()->getLevel() < sWorld->getIntConfig(CONFIG_TICKET_LEVEL_REQ))
{
SendNotification(GetTrinityString(LANG_TICKET_REQ), sWorld->getIntConfig(CONFIG_TICKET_LEVEL_REQ));
return;
}
GMTicketResponse response = GMTICKET_RESPONSE_FAILURE;
// Player must not have ticket
if (!sTicketMgr->GetTicketByPlayer(GetPlayer()->GetGUID()))
{
GmTicket* ticket = new GmTicket(GetPlayer(), recv_data);
sTicketMgr->AddTicket(ticket);
sTicketMgr->UpdateLastChange();
sWorld->SendGMText(LANG_COMMAND_TICKETNEW, GetPlayer()->GetName(), ticket->GetId());
response = GMTICKET_RESPONSE_SUCCESS;
}
WorldPacket data(SMSG_GMTICKET_CREATE, 4);
data << uint32(response);
SendPacket(&data);
}
void WorldSession::HandleGMTicketUpdateOpcode(WorldPacket & recv_data)
{
std::string message;
recv_data >> message;
GMTicketResponse response = GMTICKET_RESPONSE_FAILURE;
if (GmTicket *ticket = sTicketMgr->GetTicketByPlayer(GetPlayer()->GetGUID()))
{
SQLTransaction trans = SQLTransaction(NULL);
ticket->SetMessage(message);
ticket->SaveToDB(trans);
sWorld->SendGMText(LANG_COMMAND_TICKETUPDATED, GetPlayer()->GetName(), ticket->GetId());
response = GMTICKET_RESPONSE_SUCCESS;
}
WorldPacket data(SMSG_GMTICKET_UPDATETEXT, 4);
data << uint32(response);
SendPacket(&data);
}
void WorldSession::HandleGMTicketDeleteOpcode(WorldPacket & /*recv_data*/)
{
if (GmTicket* ticket = sTicketMgr->GetTicketByPlayer(GetPlayer()->GetGUID()))
{
WorldPacket data(SMSG_GMTICKET_DELETETICKET, 4);
data << uint32(GMTICKET_RESPONSE_TICKET_DELETED);
SendPacket(&data);
sWorld->SendGMText(LANG_COMMAND_TICKETPLAYERABANDON, GetPlayer()->GetName(), ticket->GetId());
sTicketMgr->CloseTicket(ticket->GetId(), GetPlayer()->GetGUID());
sTicketMgr->SendTicket(this, NULL);
}
}
void WorldSession::HandleGMTicketGetTicketOpcode(WorldPacket & /*recv_data*/)
{
SendQueryTimeResponse();
if (GmTicket* ticket = sTicketMgr->GetTicketByPlayer(GetPlayer()->GetGUID()))
{
if (ticket->IsCompleted())
ticket->SendResponse(this);
else
sTicketMgr->SendTicket(this, ticket);
}
else
sTicketMgr->SendTicket(this, NULL);
}
void WorldSession::HandleGMTicketSystemStatusOpcode(WorldPacket & /*recv_data*/)
{
// Note: This only disables the ticket UI at client side and is not fully reliable
// are we sure this is a uint32? Should ask Zor
WorldPacket data(SMSG_GMTICKET_SYSTEMSTATUS, 4);
data << uint32(sTicketMgr->GetStatus() ? GMTICKET_QUEUE_STATUS_ENABLED : GMTICKET_QUEUE_STATUS_DISABLED);
SendPacket(&data);
}
void WorldSession::HandleGMSurveySubmit(WorldPacket& recv_data)
{
uint32 nextSurveyID = sTicketMgr->GetNextSurveyID();
// just put the survey into the database
uint32 mainSurvey; // GMSurveyCurrentSurvey.dbc, column 1 (all 9) ref to GMSurveySurveys.dbc
recv_data >> mainSurvey;
// sub_survey1, r1, comment1, sub_survey2, r2, comment2, sub_survey3, r3, comment3, sub_survey4, r4, comment4, sub_survey5, r5, comment5, sub_survey6, r6, comment6, sub_survey7, r7, comment7, sub_survey8, r8, comment8, sub_survey9, r9, comment9, sub_survey10, r10, comment10,
for (uint8 i = 0; i < 10; i++)
{
uint32 subSurveyId; // ref to i'th GMSurveySurveys.dbc field (all fields in that dbc point to fields in GMSurveyQuestions.dbc)
recv_data >> subSurveyId;
if (!subSurveyId)
break;
uint8 rank; // probably some sort of ref to GMSurveyAnswers.dbc
recv_data >> rank;
std::string comment; // comment ("Usage: GMSurveyAnswerSubmit(question, rank, comment)")
recv_data >> comment;
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GM_SUBSURVEY);
stmt->setUInt32(0, nextSurveyID);
stmt->setUInt32(1, subSurveyId);
stmt->setUInt32(2, rank);
stmt->setString(3, comment);
CharacterDatabase.Execute(stmt);
}
std::string comment; // just a guess
recv_data >> comment;
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GM_SURVEY);
stmt->setUInt32(0, GUID_LOPART(GetPlayer()->GetGUID()));
stmt->setUInt32(1, nextSurveyID);
stmt->setUInt32(2, mainSurvey);
stmt->setString(3, comment);
CharacterDatabase.Execute(stmt);
}
void WorldSession::HandleReportLag(WorldPacket& recv_data)
{
// just put the lag report into the database...
// can't think of anything else to do with it
uint32 lagType, mapId;
recv_data >> lagType;
recv_data >> mapId;
float x, y, z;
recv_data >> x;
recv_data >> y;
recv_data >> z;
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_LAG_REPORT);
stmt->setUInt32(0, GUID_LOPART(GetPlayer()->GetGUID()));
stmt->setUInt8 (1, lagType);
stmt->setUInt16(2, mapId);
stmt->setFloat (3, x);
stmt->setFloat (4, y);
stmt->setFloat (5, z);
CharacterDatabase.Execute(stmt);
}
void WorldSession::HandleGMResponseResolve(WorldPacket& /*recvPacket*/)
{
// empty packet
if (GmTicket* ticket = sTicketMgr->GetTicketByPlayer(GetPlayer()->GetGUID()))
{
uint8 getSurvey = 0;
if (float(rand_chance()) < sWorld->getFloatConfig(CONFIG_CHANCE_OF_GM_SURVEY))
getSurvey = 1;
WorldPacket data(SMSG_GMRESPONSE_STATUS_UPDATE, 4);
data << uint8(getSurvey);
SendPacket(&data);
WorldPacket data2(SMSG_GMTICKET_DELETETICKET, 4);
data2 << uint32(GMTICKET_RESPONSE_TICKET_DELETED);
SendPacket(&data2);
sTicketMgr->CloseTicket(ticket->GetId(), GetPlayer()->GetGUID());
sTicketMgr->SendTicket(this, NULL);
}
}
+731
View File
@@ -0,0 +1,731 @@
/*
* Copyright (C) 2008-2012 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 "WorldPacket.h"
#include "WorldSession.h"
#include "World.h"
#include "ObjectAccessor.h"
#include "Log.h"
#include "Opcodes.h"
#include "Player.h"
#include "Item.h"
#include "Spell.h"
#include "SocialMgr.h"
#include "Language.h"
#include "AccountMgr.h"
void WorldSession::SendTradeStatus(TradeStatus status)
{
WorldPacket data;
switch (status)
{
case TRADE_STATUS_BEGIN_TRADE:
data.Initialize(SMSG_TRADE_STATUS, 4+8);
data << uint32(status);
data << uint64(0);
break;
case TRADE_STATUS_OPEN_WINDOW:
data.Initialize(SMSG_TRADE_STATUS, 4+4);
data << uint32(status);
data << uint32(0); // added in 2.4.0
break;
case TRADE_STATUS_CLOSE_WINDOW:
data.Initialize(SMSG_TRADE_STATUS, 4+4+1+4);
data << uint32(status);
data << uint32(0);
data << uint8(0);
data << uint32(0);
break;
case TRADE_STATUS_ONLY_CONJURED:
case TRADE_STATUS_NOT_ELIGIBLE:
data.Initialize(SMSG_TRADE_STATUS, 4+1);
data << uint32(status);
data << uint8(0);
break;
default:
data.Initialize(SMSG_TRADE_STATUS, 4);
data << uint32(status);
break;
}
SendPacket(&data);
}
void WorldSession::HandleIgnoreTradeOpcode(WorldPacket& /*recvPacket*/)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Ignore Trade %u", _player->GetGUIDLow());
// recvPacket.print_storage();
}
void WorldSession::HandleBusyTradeOpcode(WorldPacket& /*recvPacket*/)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Busy Trade %u", _player->GetGUIDLow());
// recvPacket.print_storage();
}
void WorldSession::SendUpdateTrade(bool trader_data /*= true*/)
{
TradeData* view_trade = trader_data ? _player->GetTradeData()->GetTraderData() : _player->GetTradeData();
WorldPacket data(SMSG_TRADE_STATUS_EXTENDED, 1+4+4+4+4+4+7*(1+4+4+4+4+8+4+4+4+4+8+4+4+4+4+4+4));
data << uint8(trader_data); // 1 means traders data, 0 means own
data << uint32(0); // added in 2.4.0, this value must be equal to value from TRADE_STATUS_OPEN_WINDOW status packet (different value for different players to block multiple trades?)
data << uint32(TRADE_SLOT_COUNT); // trade slots count/number?, = next field in most cases
data << uint32(TRADE_SLOT_COUNT); // trade slots count/number?, = prev field in most cases
data << uint32(view_trade->GetMoney()); // trader gold
data << uint32(view_trade->GetSpell()); // spell casted on lowest slot item
for (uint8 i = 0; i < TRADE_SLOT_COUNT; ++i)
{
data << uint8(i); // trade slot number, if not specified, then end of packet
if (Item* item = view_trade->GetItem(TradeSlots(i)))
{
data << uint32(item->GetTemplate()->ItemId); // entry
data << uint32(item->GetTemplate()->DisplayInfoID);// display id
data << uint32(item->GetCount()); // stack count
// wrapped: hide stats but show giftcreator name
data << uint32(item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED) ? 1 : 0);
data << uint64(item->GetUInt64Value(ITEM_FIELD_GIFTCREATOR));
// perm. enchantment and gems
data << uint32(item->GetEnchantmentId(PERM_ENCHANTMENT_SLOT));
for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+MAX_GEM_SOCKETS; ++enchant_slot)
data << uint32(item->GetEnchantmentId(EnchantmentSlot(enchant_slot)));
// creator
data << uint64(item->GetUInt64Value(ITEM_FIELD_CREATOR));
data << uint32(item->GetSpellCharges()); // charges
data << uint32(item->GetItemSuffixFactor()); // SuffixFactor
data << uint32(item->GetItemRandomPropertyId());// random properties id
data << uint32(item->GetTemplate()->LockID); // lock id
// max durability
data << uint32(item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY));
// durability
data << uint32(item->GetUInt32Value(ITEM_FIELD_DURABILITY));
}
else
{
for (uint8 j = 0; j < 18; ++j)
data << uint32(0);
}
}
SendPacket(&data);
}
//==============================================================
// transfer the items to the players
void WorldSession::moveItems(Item* myItems[], Item* hisItems[])
{
Player* trader = _player->GetTrader();
if (!trader)
return;
for (uint8 i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i)
{
ItemPosCountVec traderDst;
ItemPosCountVec playerDst;
bool traderCanTrade = (myItems[i] == NULL || trader->CanStoreItem(NULL_BAG, NULL_SLOT, traderDst, myItems[i], false) == EQUIP_ERR_OK);
bool playerCanTrade = (hisItems[i] == NULL || _player->CanStoreItem(NULL_BAG, NULL_SLOT, playerDst, hisItems[i], false) == EQUIP_ERR_OK);
if (traderCanTrade && playerCanTrade)
{
// Ok, if trade item exists and can be stored
// If we trade in both directions we had to check, if the trade will work before we actually do it
// A roll back is not possible after we stored it
if (myItems[i])
{
// logging
sLog->outDebug(LOG_FILTER_NETWORKIO, "partner storing: %u", myItems[i]->GetGUIDLow());
if (!AccountMgr::IsPlayerAccount(_player->GetSession()->GetSecurity()) && sWorld->getBoolConfig(CONFIG_GM_LOG_TRADE))
{
sLog->outCommand(_player->GetSession()->GetAccountId(), "GM %s (Account: %u) trade: %s (Entry: %d Count: %u) to player: %s (Account: %u)",
_player->GetName(), _player->GetSession()->GetAccountId(),
myItems[i]->GetTemplate()->Name1.c_str(), myItems[i]->GetEntry(), myItems[i]->GetCount(),
trader->GetName(), trader->GetSession()->GetAccountId());
}
// adjust time (depends on /played)
if (myItems[i]->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_BOP_TRADEABLE))
myItems[i]->SetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME, trader->GetTotalPlayedTime()-(_player->GetTotalPlayedTime()-myItems[i]->GetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME)));
// store
trader->MoveItemToInventory(traderDst, myItems[i], true, true);
}
if (hisItems[i])
{
// logging
sLog->outDebug(LOG_FILTER_NETWORKIO, "player storing: %u", hisItems[i]->GetGUIDLow());
if (!AccountMgr::IsPlayerAccount(trader->GetSession()->GetSecurity()) && sWorld->getBoolConfig(CONFIG_GM_LOG_TRADE))
{
sLog->outCommand(trader->GetSession()->GetAccountId(), "GM %s (Account: %u) trade: %s (Entry: %d Count: %u) to player: %s (Account: %u)",
trader->GetName(), trader->GetSession()->GetAccountId(),
hisItems[i]->GetTemplate()->Name1.c_str(), hisItems[i]->GetEntry(), hisItems[i]->GetCount(),
_player->GetName(), _player->GetSession()->GetAccountId());
}
// adjust time (depends on /played)
if (hisItems[i]->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_BOP_TRADEABLE))
hisItems[i]->SetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME, _player->GetTotalPlayedTime()-(trader->GetTotalPlayedTime()-hisItems[i]->GetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME)));
// store
_player->MoveItemToInventory(playerDst, hisItems[i], true, true);
}
}
else
{
// in case of fatal error log error message
// return the already removed items to the original owner
if (myItems[i])
{
if (!traderCanTrade)
sLog->outError("trader can't store item: %u", myItems[i]->GetGUIDLow());
if (_player->CanStoreItem(NULL_BAG, NULL_SLOT, playerDst, myItems[i], false) == EQUIP_ERR_OK)
_player->MoveItemToInventory(playerDst, myItems[i], true, true);
else
sLog->outError("player can't take item back: %u", myItems[i]->GetGUIDLow());
}
// return the already removed items to the original owner
if (hisItems[i])
{
if (!playerCanTrade)
sLog->outError("player can't store item: %u", hisItems[i]->GetGUIDLow());
if (trader->CanStoreItem(NULL_BAG, NULL_SLOT, traderDst, hisItems[i], false) == EQUIP_ERR_OK)
trader->MoveItemToInventory(traderDst, hisItems[i], true, true);
else
sLog->outError("trader can't take item back: %u", hisItems[i]->GetGUIDLow());
}
}
}
}
//==============================================================
static void setAcceptTradeMode(TradeData* myTrade, TradeData* hisTrade, Item* *myItems, Item* *hisItems)
{
myTrade->SetInAcceptProcess(true);
hisTrade->SetInAcceptProcess(true);
// store items in local list and set 'in-trade' flag
for (uint8 i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i)
{
if (Item* item = myTrade->GetItem(TradeSlots(i)))
{
sLog->outStaticDebug("player trade item %u bag: %u slot: %u", item->GetGUIDLow(), item->GetBagSlot(), item->GetSlot());
//Can return NULL
myItems[i] = item;
myItems[i]->SetInTrade();
}
if (Item* item = hisTrade->GetItem(TradeSlots(i)))
{
sLog->outStaticDebug("partner trade item %u bag: %u slot: %u", item->GetGUIDLow(), item->GetBagSlot(), item->GetSlot());
hisItems[i] = item;
hisItems[i]->SetInTrade();
}
}
}
static void clearAcceptTradeMode(TradeData* myTrade, TradeData* hisTrade)
{
myTrade->SetInAcceptProcess(false);
hisTrade->SetInAcceptProcess(false);
}
static void clearAcceptTradeMode(Item* *myItems, Item* *hisItems)
{
// clear 'in-trade' flag
for (uint8 i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i)
{
if (myItems[i])
myItems[i]->SetInTrade(false);
if (hisItems[i])
hisItems[i]->SetInTrade(false);
}
}
void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/)
{
TradeData* my_trade = _player->m_trade;
if (!my_trade)
return;
Player* trader = my_trade->GetTrader();
TradeData* his_trade = trader->m_trade;
if (!his_trade)
return;
Item* myItems[TRADE_SLOT_TRADED_COUNT] = { NULL, NULL, NULL, NULL, NULL, NULL };
Item* hisItems[TRADE_SLOT_TRADED_COUNT] = { NULL, NULL, NULL, NULL, NULL, NULL };
bool myCanCompleteTrade = true, hisCanCompleteTrade = true;
// set before checks for propertly undo at problems (it already set in to client)
my_trade->SetAccepted(true);
// not accept case incorrect money amount
if (!_player->HasEnoughMoney(my_trade->GetMoney()))
{
SendNotification(LANG_NOT_ENOUGH_GOLD);
my_trade->SetAccepted(false, true);
return;
}
// not accept case incorrect money amount
if (!trader->HasEnoughMoney(his_trade->GetMoney()))
{
trader->GetSession()->SendNotification(LANG_NOT_ENOUGH_GOLD);
his_trade->SetAccepted(false, true);
return;
}
// not accept if some items now can't be trade (cheating)
for (uint8 i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i)
{
if (Item* item = my_trade->GetItem(TradeSlots(i)))
{
if (!item->CanBeTraded(false, true))
{
SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
return;
}
if (item->IsBindedNotWith(trader))
{
SendTradeStatus(TRADE_STATUS_NOT_ELIGIBLE);
SendTradeStatus(TRADE_STATUS_CLOSE_WINDOW/*TRADE_STATUS_TRADE_CANCELED*/);
return;
}
}
if (Item* item = his_trade->GetItem(TradeSlots(i)))
{
if (!item->CanBeTraded(false, true))
{
SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
return;
}
//if (item->IsBindedNotWith(_player)) // dont mark as invalid when his item isnt good (not exploitable because if item is invalid trade will fail anyway later on the same check)
//{
// SendTradeStatus(TRADE_STATUS_NOT_ELIGIBLE);
// his_trade->SetAccepted(false, true);
// return;
//}
}
}
if (his_trade->IsAccepted())
{
setAcceptTradeMode(my_trade, his_trade, myItems, hisItems);
Spell* my_spell = NULL;
SpellCastTargets my_targets;
Spell* his_spell = NULL;
SpellCastTargets his_targets;
// not accept if spell can't be casted now (cheating)
if (uint32 my_spell_id = my_trade->GetSpell())
{
SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(my_spell_id);
Item* castItem = my_trade->GetSpellCastItem();
if (!spellEntry || !his_trade->GetItem(TRADE_SLOT_NONTRADED) ||
(my_trade->HasSpellCastItem() && !castItem))
{
clearAcceptTradeMode(my_trade, his_trade);
clearAcceptTradeMode(myItems, hisItems);
my_trade->SetSpell(0);
return;
}
my_spell = new Spell(_player, spellEntry, TRIGGERED_FULL_MASK);
my_spell->m_CastItem = castItem;
my_targets.SetTradeItemTarget(_player);
my_spell->m_targets = my_targets;
SpellCastResult res = my_spell->CheckCast(true);
if (res != SPELL_CAST_OK)
{
my_spell->SendCastResult(res);
clearAcceptTradeMode(my_trade, his_trade);
clearAcceptTradeMode(myItems, hisItems);
delete my_spell;
my_trade->SetSpell(0);
return;
}
}
// not accept if spell can't be casted now (cheating)
if (uint32 his_spell_id = his_trade->GetSpell())
{
SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(his_spell_id);
Item* castItem = his_trade->GetSpellCastItem();
if (!spellEntry || !my_trade->GetItem(TRADE_SLOT_NONTRADED) || (his_trade->HasSpellCastItem() && !castItem))
{
delete my_spell;
his_trade->SetSpell(0);
clearAcceptTradeMode(my_trade, his_trade);
clearAcceptTradeMode(myItems, hisItems);
return;
}
his_spell = new Spell(trader, spellEntry, TRIGGERED_FULL_MASK);
his_spell->m_CastItem = castItem;
his_targets.SetTradeItemTarget(trader);
his_spell->m_targets = his_targets;
SpellCastResult res = his_spell->CheckCast(true);
if (res != SPELL_CAST_OK)
{
his_spell->SendCastResult(res);
clearAcceptTradeMode(my_trade, his_trade);
clearAcceptTradeMode(myItems, hisItems);
delete my_spell;
delete his_spell;
his_trade->SetSpell(0);
return;
}
}
// inform partner client
trader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_ACCEPT);
// test if item will fit in each inventory
hisCanCompleteTrade = (trader->CanStoreItems(myItems, TRADE_SLOT_TRADED_COUNT) == EQUIP_ERR_OK);
myCanCompleteTrade = (_player->CanStoreItems(hisItems, TRADE_SLOT_TRADED_COUNT) == EQUIP_ERR_OK);
clearAcceptTradeMode(myItems, hisItems);
// in case of missing space report error
if (!myCanCompleteTrade)
{
clearAcceptTradeMode(my_trade, his_trade);
SendNotification(LANG_NOT_FREE_TRADE_SLOTS);
trader->GetSession()->SendNotification(LANG_NOT_PARTNER_FREE_TRADE_SLOTS);
my_trade->SetAccepted(false);
his_trade->SetAccepted(false);
return;
}
else if (!hisCanCompleteTrade)
{
clearAcceptTradeMode(my_trade, his_trade);
SendNotification(LANG_NOT_PARTNER_FREE_TRADE_SLOTS);
trader->GetSession()->SendNotification(LANG_NOT_FREE_TRADE_SLOTS);
my_trade->SetAccepted(false);
his_trade->SetAccepted(false);
return;
}
// execute trade: 1. remove
for (uint8 i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i)
{
if (myItems[i])
{
myItems[i]->SetUInt64Value(ITEM_FIELD_GIFTCREATOR, _player->GetGUID());
_player->MoveItemFromInventory(myItems[i]->GetBagSlot(), myItems[i]->GetSlot(), true);
}
if (hisItems[i])
{
hisItems[i]->SetUInt64Value(ITEM_FIELD_GIFTCREATOR, trader->GetGUID());
trader->MoveItemFromInventory(hisItems[i]->GetBagSlot(), hisItems[i]->GetSlot(), true);
}
}
// execute trade: 2. store
moveItems(myItems, hisItems);
// logging money
if (sWorld->getBoolConfig(CONFIG_GM_LOG_TRADE))
{
if (!AccountMgr::IsPlayerAccount(_player->GetSession()->GetSecurity()) && my_trade->GetMoney() > 0)
{
sLog->outCommand(_player->GetSession()->GetAccountId(), "GM %s (Account: %u) give money (Amount: %u) to player: %s (Account: %u)",
_player->GetName(), _player->GetSession()->GetAccountId(),
my_trade->GetMoney(),
trader->GetName(), trader->GetSession()->GetAccountId());
}
if (!AccountMgr::IsPlayerAccount(trader->GetSession()->GetSecurity()) && his_trade->GetMoney() > 0)
{
sLog->outCommand(trader->GetSession()->GetAccountId(), "GM %s (Account: %u) give money (Amount: %u) to player: %s (Account: %u)",
trader->GetName(), trader->GetSession()->GetAccountId(),
his_trade->GetMoney(),
_player->GetName(), _player->GetSession()->GetAccountId());
}
}
// update money
_player->ModifyMoney(-int32(my_trade->GetMoney()));
_player->ModifyMoney(his_trade->GetMoney());
trader->ModifyMoney(-int32(his_trade->GetMoney()));
trader->ModifyMoney(my_trade->GetMoney());
if (my_spell)
my_spell->prepare(&my_targets);
if (his_spell)
his_spell->prepare(&his_targets);
// cleanup
clearAcceptTradeMode(my_trade, his_trade);
delete _player->m_trade;
_player->m_trade = NULL;
delete trader->m_trade;
trader->m_trade = NULL;
// desynchronized with the other saves here (SaveInventoryAndGoldToDB() not have own transaction guards)
SQLTransaction trans = CharacterDatabase.BeginTransaction();
_player->SaveInventoryAndGoldToDB(trans);
trader->SaveInventoryAndGoldToDB(trans);
CharacterDatabase.CommitTransaction(trans);
trader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_COMPLETE);
SendTradeStatus(TRADE_STATUS_TRADE_COMPLETE);
}
else
{
trader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_ACCEPT);
}
}
void WorldSession::HandleUnacceptTradeOpcode(WorldPacket& /*recvPacket*/)
{
TradeData* my_trade = _player->GetTradeData();
if (!my_trade)
return;
my_trade->SetAccepted(false, true);
}
void WorldSession::HandleBeginTradeOpcode(WorldPacket& /*recvPacket*/)
{
TradeData* my_trade = _player->m_trade;
if (!my_trade)
return;
my_trade->GetTrader()->GetSession()->SendTradeStatus(TRADE_STATUS_OPEN_WINDOW);
SendTradeStatus(TRADE_STATUS_OPEN_WINDOW);
}
void WorldSession::SendCancelTrade()
{
if (m_playerRecentlyLogout)
return;
SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
}
void WorldSession::HandleCancelTradeOpcode(WorldPacket& /*recvPacket*/)
{
// sended also after LOGOUT COMPLETE
if (_player) // needed because STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT
_player->TradeCancel(true);
}
void WorldSession::HandleInitiateTradeOpcode(WorldPacket& recvPacket)
{
if (GetPlayer()->m_trade)
{
recvPacket.rfinish();
return;
}
uint64 ID;
recvPacket >> ID;
if (!GetPlayer()->isAlive())
{
SendTradeStatus(TRADE_STATUS_YOU_DEAD);
return;
}
if (GetPlayer()->HasUnitState(UNIT_STAT_STUNNED))
{
SendTradeStatus(TRADE_STATUS_YOU_STUNNED);
return;
}
if (isLogingOut())
{
SendTradeStatus(TRADE_STATUS_YOU_LOGOUT);
return;
}
if (GetPlayer()->isInFlight())
{
SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR);
return;
}
if (GetPlayer()->getLevel() < sWorld->getIntConfig(CONFIG_TRADE_LEVEL_REQ))
{
SendNotification(GetTrinityString(LANG_TRADE_REQ), sWorld->getIntConfig(CONFIG_TRADE_LEVEL_REQ));
return;
}
Player* pOther = ObjectAccessor::FindPlayer(ID);
if (!pOther)
{
SendTradeStatus(TRADE_STATUS_NO_TARGET);
return;
}
if (pOther == GetPlayer() || pOther->m_trade)
{
SendTradeStatus(TRADE_STATUS_BUSY);
return;
}
if (!pOther->isAlive())
{
SendTradeStatus(TRADE_STATUS_TARGET_DEAD);
return;
}
if (pOther->isInFlight())
{
SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR);
return;
}
if (pOther->HasUnitState(UNIT_STAT_STUNNED))
{
SendTradeStatus(TRADE_STATUS_TARGET_STUNNED);
return;
}
if (pOther->GetSession()->isLogingOut())
{
SendTradeStatus(TRADE_STATUS_TARGET_LOGOUT);
return;
}
if (pOther->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow()))
{
SendTradeStatus(TRADE_STATUS_IGNORE_YOU);
return;
}
if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_TRADE) && pOther->GetTeam() !=_player->GetTeam())
{
SendTradeStatus(TRADE_STATUS_WRONG_FACTION);
return;
}
if (!pOther->IsWithinDistInMap(_player, 10.0f, false))
{
SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR);
return;
}
if (pOther->getLevel() < sWorld->getIntConfig(CONFIG_TRADE_LEVEL_REQ))
{
SendNotification(GetTrinityString(LANG_TRADE_OTHER_REQ), sWorld->getIntConfig(CONFIG_TRADE_LEVEL_REQ));
return;
}
// OK start trade
_player->m_trade = new TradeData(_player, pOther);
pOther->m_trade = new TradeData(pOther, _player);
WorldPacket data(SMSG_TRADE_STATUS, 12);
data << uint32(TRADE_STATUS_BEGIN_TRADE);
data << uint64(_player->GetGUID());
pOther->GetSession()->SendPacket(&data);
}
void WorldSession::HandleSetTradeGoldOpcode(WorldPacket& recvPacket)
{
uint32 gold;
recvPacket >> gold;
TradeData* my_trade = _player->GetTradeData();
if (!my_trade)
return;
// gold can be incorrect, but this is checked at trade finished.
my_trade->SetMoney(gold);
}
void WorldSession::HandleSetTradeItemOpcode(WorldPacket& recvPacket)
{
// send update
uint8 tradeSlot;
uint8 bag;
uint8 slot;
recvPacket >> tradeSlot;
recvPacket >> bag;
recvPacket >> slot;
TradeData* my_trade = _player->GetTradeData();
if (!my_trade)
return;
// invalid slot number
if (tradeSlot >= TRADE_SLOT_COUNT)
{
SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
return;
}
// check cheating, can't fail with correct client operations
Item* item = _player->GetItemByPos(bag, slot);
if (!item || (tradeSlot != TRADE_SLOT_NONTRADED && !item->CanBeTraded(false, true)))
{
SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
return;
}
uint64 iGUID = item->GetGUID();
// prevent place single item into many trade slots using cheating and client bugs
if (my_trade->HasItem(iGUID))
{
// cheating attempt
SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
return;
}
my_trade->SetItem(TradeSlots(tradeSlot), item);
}
void WorldSession::HandleClearTradeItemOpcode(WorldPacket& recvPacket)
{
uint8 tradeSlot;
recvPacket >> tradeSlot;
TradeData* my_trade = _player->m_trade;
if (!my_trade)
return;
// invalid slot number
if (tradeSlot >= TRADE_SLOT_COUNT)
return;
my_trade->SetItem(TradeSlots(tradeSlot), NULL);
}
+225
View File
@@ -0,0 +1,225 @@
/*
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
*
* 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 "WorldPacket.h"
#include "WorldSession.h"
#include "Opcodes.h"
#include "Vehicle.h"
#include "Player.h"
#include "Log.h"
#include "ObjectAccessor.h"
void WorldSession::HandleDismissControlledVehicle(WorldPacket &recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_DISMISS_CONTROLLED_VEHICLE");
uint64 vehicleGUID = _player->GetCharmGUID();
if (!vehicleGUID) // something wrong here...
{
recv_data.rfinish(); // prevent warnings spam
return;
}
uint64 guid;
recv_data.readPackGUID(guid);
MovementInfo mi;
mi.guid = guid;
ReadMovementInfo(recv_data, &mi);
_player->m_movementInfo = mi;
_player->ExitVehicle();
}
void WorldSession::HandleChangeSeatsOnControlledVehicle(WorldPacket &recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_CHANGE_SEATS_ON_CONTROLLED_VEHICLE");
Unit* vehicle_base = GetPlayer()->GetVehicleBase();
if (!vehicle_base)
{
recv_data.rfinish(); // prevent warnings spam
return;
}
VehicleSeatEntry const* seat = GetPlayer()->GetVehicle()->GetSeatForPassenger(GetPlayer());
if (!seat->CanSwitchFromSeat())
{
recv_data.rfinish(); // prevent warnings spam
sLog->outError("HandleChangeSeatsOnControlledVehicle, Opcode: %u, Player %u tried to switch seats but current seatflags %u don't permit that.",
recv_data.GetOpcode(), GetPlayer()->GetGUIDLow(), seat->m_flags);
return;
}
switch (recv_data.GetOpcode())
{
case CMSG_REQUEST_VEHICLE_PREV_SEAT:
GetPlayer()->ChangeSeat(-1, false);
break;
case CMSG_REQUEST_VEHICLE_NEXT_SEAT:
GetPlayer()->ChangeSeat(-1, true);
break;
case CMSG_CHANGE_SEATS_ON_CONTROLLED_VEHICLE:
{
uint64 guid; // current vehicle guid
recv_data.readPackGUID(guid);
ReadMovementInfo(recv_data, &vehicle_base->m_movementInfo);
uint64 accessory; // accessory guid
recv_data.readPackGUID(accessory);
int8 seatId;
recv_data >> seatId;
if (vehicle_base->GetGUID() != guid)
return;
if (!accessory)
GetPlayer()->ChangeSeat(-1, seatId > 0); // prev/next
else if (Unit* vehUnit = Unit::GetUnit(*GetPlayer(), accessory))
{
if (Vehicle* vehicle = vehUnit->GetVehicleKit())
if (vehicle->HasEmptySeat(seatId))
vehUnit->HandleSpellClick(GetPlayer(), seatId);
}
break;
}
case CMSG_REQUEST_VEHICLE_SWITCH_SEAT:
{
uint64 guid; // current vehicle guid
recv_data.readPackGUID(guid);
int8 seatId;
recv_data >> seatId;
if (vehicle_base->GetGUID() == guid)
GetPlayer()->ChangeSeat(seatId);
else if (Unit* vehUnit = Unit::GetUnit(*GetPlayer(), guid))
if (Vehicle* vehicle = vehUnit->GetVehicleKit())
if (vehicle->HasEmptySeat(seatId))
vehUnit->HandleSpellClick(GetPlayer(), seatId);
break;
}
default:
break;
}
}
void WorldSession::HandleEnterPlayerVehicle(WorldPacket &data)
{
// Read guid
uint64 guid;
data >> guid;
if (Player* player = ObjectAccessor::FindPlayer(guid))
{
if (!player->GetVehicleKit())
return;
if (!player->IsInRaidWith(_player))
return;
if (!player->IsWithinDistInMap(_player, INTERACTION_DISTANCE))
return;
_player->EnterVehicle(player);
}
}
void WorldSession::HandleEjectPassenger(WorldPacket &data)
{
Vehicle* vehicle = _player->GetVehicleKit();
if (!vehicle)
{
data.rfinish(); // prevent warnings spam
sLog->outError("HandleEjectPassenger: Player %u is not in a vehicle!", GetPlayer()->GetGUIDLow());
return;
}
uint64 guid;
data >> guid;
if (IS_PLAYER_GUID(guid))
{
Player* player = ObjectAccessor::FindPlayer(guid);
if (!player)
{
sLog->outError("Player %u tried to eject player %u from vehicle, but the latter was not found in world!", GetPlayer()->GetGUIDLow(), GUID_LOPART(guid));
return;
}
if (!player->IsOnVehicle(vehicle->GetBase()))
{
sLog->outError("Player %u tried to eject player %u, but they are not in the same vehicle", GetPlayer()->GetGUIDLow(), GUID_LOPART(guid));
return;
}
VehicleSeatEntry const* seat = vehicle->GetSeatForPassenger(player);
ASSERT(seat);
if (seat->IsEjectable())
player->ExitVehicle();
else
sLog->outError("Player %u attempted to eject player %u from non-ejectable seat.", GetPlayer()->GetGUIDLow(), GUID_LOPART(guid));
}
else if (IS_CREATURE_GUID(guid))
{
Unit* unit = ObjectAccessor::GetUnit(*_player, guid);
if (!unit) // creatures can be ejected too from player mounts
{
sLog->outError("Player %u tried to eject creature guid %u from vehicle, but the latter was not found in world!", GetPlayer()->GetGUIDLow(), GUID_LOPART(guid));
return;
}
if (!unit->IsOnVehicle(vehicle->GetBase()))
{
sLog->outError("Player %u tried to eject unit %u, but they are not in the same vehicle", GetPlayer()->GetGUIDLow(), GUID_LOPART(guid));
return;
}
VehicleSeatEntry const* seat = vehicle->GetSeatForPassenger(unit);
ASSERT(seat);
if (seat->IsEjectable())
{
ASSERT(GetPlayer() == vehicle->GetBase());
unit->ExitVehicle();
}
else
sLog->outError("Player %u attempted to eject creature GUID %u from non-ejectable seat.", GetPlayer()->GetGUIDLow(), GUID_LOPART(guid));
}
else
sLog->outError("HandleEjectPassenger: Player %u tried to eject invalid GUID "UI64FMTD, GetPlayer()->GetGUIDLow(), guid);
}
void WorldSession::HandleRequestVehicleExit(WorldPacket& /*recv_data*/)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_REQUEST_VEHICLE_EXIT");
if (Vehicle* vehicle = GetPlayer()->GetVehicle())
{
if (VehicleSeatEntry const* seat = vehicle->GetSeatForPassenger(GetPlayer()))
{
if (seat->CanEnterOrExit())
GetPlayer()->ExitVehicle();
else
sLog->outError("Player %u tried to exit vehicle, but seatflags %u (ID: %u) don't permit that.",
GetPlayer()->GetGUIDLow(), seat->m_ID, seat->m_flags);
}
}
}
+45
View File
@@ -0,0 +1,45 @@
/*
* Copyright (C) 2008-2012 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 "WorldPacket.h"
#include "WorldSession.h"
#include "Opcodes.h"
#include "Log.h"
void WorldSession::HandleVoiceSessionEnableOpcode(WorldPacket& recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_VOICE_SESSION_ENABLE");
// uint8 isVoiceEnabled, uint8 isMicrophoneEnabled
recv_data.read_skip<uint8>();
recv_data.read_skip<uint8>();
}
void WorldSession::HandleChannelVoiceOnOpcode(WorldPacket& /*recv_data*/)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CHANNEL_VOICE_ON");
// Enable Voice button in channel context menu
}
void WorldSession::HandleSetActiveVoiceChannel(WorldPacket& recv_data)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_SET_ACTIVE_VOICE_CHANNEL");
recv_data.read_skip<uint32>();
recv_data.read_skip<char*>();
}