* Even more restructuring of the game library.

--HG--
branch : trunk
This commit is contained in:
XTZGZoReX
2010-06-06 22:55:56 +02:00
parent 840d4ab99d
commit 62200e8d02
169 changed files with 0 additions and 0 deletions
@@ -0,0 +1,234 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 Trinity <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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "zlib/zlib.h"
#include "AddonHandler.h"
#include "Database/DatabaseEnv.h"
#include "Policies/SingletonImp.h"
#include "Opcodes.h"
#include "Log.h"
INSTANTIATE_SINGLETON_1( AddonHandler );
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("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 unk2 = (crc != 0x4c1c776d); // If addon is Standard addon CRC
*Target << uint8(unk2);
if (unk2)
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);
/*for(uint32 i = 0; i < count; ++i)
{
uint32
string (16 bytes)
string (16 bytes)
uint32
}*/
if(AddOnPacked.rpos() != AddOnPacked.size())
sLog.outDebug("packet under read!");
}
else
{
sLog.outError("Addon packet uncompress error :(");
return false;
}
return true;
}
/* Code use in 1.10.2 when client not ignore ban state sended for addons. Saved for reference if client switch to use server ban state information
void AddonHandler::BuildAddonPacket(WorldPacket* Source, WorldPacket* Target, uint32 Packetoffset)
{
ByteBuffer AddOnPacked;
uLongf AddonRealSize;
uint32 CurrentPosition;
uint32 TempValue;
*Source >> TempValue; //get real size of the packed structure
AddonRealSize = TempValue; //temp value becouse 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((uint8*)AddOnPacked.contents(), &AddonRealSize, (uint8*)(*Source).contents() + CurrentPosition, (*Source).size() - CurrentPosition)!= Z_OK)
{
bool* AddonAllowed = new bool; //handle addon check and enable-ing
uint32 Unknown1;
uint8 Unknown0;
AddOnPacked >> Unknown0;
AddOnPacked >> Unknown1;
Target->Initialize(SMSG_ADDON_INFO);
uint32 i = 5; //offset for addon extraction
while(i != AddOnPacked.size())
{
std::string AddonNames;
AddOns* Addonstr = new AddOns;
uint8 unk6;
uint64 CRCCHECK;
AddOnPacked >> AddonNames >> CRCCHECK >> unk6;
//sLog.outDebug("ADDON: Name:%s CRC:%x Unknown:%x",AddonNames.c_str(), CRCCHECK,unk6);
Addonstr->Name = AddonNames;
Addonstr->CRC = CRCCHECK;
//if not allowed but unknown added to list
if (GetAddonStatus(Addonstr, AddonAllowed)) // If addon is new
{
Addonstr->Enabled = m_Addon_Default; // by default new addons are set from Config file
*AddonAllowed = m_Addon_Default; // Set addon allowed on default value
_AddAddon(Addonstr);
sLog.outDetail("Found new Addon, Name:%s CRC:%x Unknown:%x",AddonNames.c_str(), CRCCHECK, unk6);
}
if (CRCCHECK == 0x4C1C776D01LL) //If addon is Standard addon CRC
{
//value's standard Addons
*Target << uint8(0) << uint8(2) << uint8(1) << uint8(0) << uint32(0);
}
else if (*AddonAllowed) //if addon is Custom addons
//value's enable addon
*Target << uint8(0x00) << uint8(0x01) << uint8(0x00) << uint8(0x01);
else
//value's disable addom
*Target << uint8(0x00) << uint8(0x0) << uint8(0x00) << uint8(0x0);
i += AddonNames.size() + 10;
}
*Target << uint8(0x0);
//delete mem allocation
delete AddonAllowed;
}
else
{
//handle uncompress error
}
}
*/
@@ -0,0 +1,41 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 Trinity <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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __ADDONHANDLER_H
#define __ADDONHANDLER_H
#include "Common.h"
#include "Config/ConfigEnv.h"
#include "Policies/Singleton.h"
#include "WorldPacket.h"
class AddonHandler
{
public:
/* Construction */
AddonHandler();
~AddonHandler();
//built addon packet
bool BuildAddonPacket(WorldPacket* Source, WorldPacket* Target);
};
#define sAddOnHandler Trinity::Singleton<AddonHandler>::Instance()
#endif
@@ -0,0 +1,391 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 Trinity <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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "Player.h"
#include "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "Database/DatabaseEnv.h"
#include "ArenaTeam.h"
#include "Log.h"
#include "ObjectMgr.h"
#include "SocialMgr.h"
void WorldSession::HandleInspectArenaTeamsOpcode(WorldPacket & recv_data)
{
sLog.outDebug("MSG_INSPECT_ARENA_TEAMS");
uint64 guid;
recv_data >> guid;
sLog.outDebug("Inspect Arena stats (GUID: %u TypeId: %u)", GUID_LOPART(guid),GuidHigh2TypeId(GUID_HIPART(guid)));
if (Player *plr = objmgr.GetPlayer(guid))
{
for (uint8 i = 0; i < MAX_ARENA_SLOT; ++i)
{
if (uint32 a_id = plr->GetArenaTeamId(i))
{
if (ArenaTeam *at = objmgr.GetArenaTeamById(a_id))
at->InspectStats(this, plr->GetGUID());
}
}
}
}
void WorldSession::HandleArenaTeamQueryOpcode(WorldPacket & recv_data)
{
sLog.outDebug("WORLD: Received CMSG_ARENA_TEAM_QUERY");
uint32 ArenaTeamId;
recv_data >> ArenaTeamId;
if (ArenaTeam *arenateam = objmgr.GetArenaTeamById(ArenaTeamId))
{
arenateam->Query(this);
arenateam->Stats(this);
}
}
void WorldSession::HandleArenaTeamRosterOpcode(WorldPacket & recv_data)
{
sLog.outDebug("WORLD: Received CMSG_ARENA_TEAM_ROSTER");
uint32 ArenaTeamId; // arena team id
recv_data >> ArenaTeamId;
if (ArenaTeam *arenateam = objmgr.GetArenaTeamById(ArenaTeamId))
arenateam->Roster(this);
}
void WorldSession::HandleArenaTeamInviteOpcode(WorldPacket & recv_data)
{
sLog.outDebug("CMSG_ARENA_TEAM_INVITE");
uint32 ArenaTeamId; // arena team id
std::string Invitedname;
Player * player = NULL;
recv_data >> ArenaTeamId >> Invitedname;
if (!Invitedname.empty())
{
if (!normalizePlayerName(Invitedname))
return;
player = ObjectAccessor::Instance().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.getConfig(CONFIG_MAX_PLAYER_LEVEL))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", player->GetName(), ERR_ARENA_TEAM_TARGET_TOO_LOW_S);
return;
}
ArenaTeam *arenateam = objmgr.GetArenaTeamById(ArenaTeamId);
if (!arenateam)
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM);
return;
}
// OK result but not send invite
if (player->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow()))
return;
if (!sWorld.getConfig(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("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("WORLD: Sent SMSG_ARENA_TEAM_INVITE");
}
void WorldSession::HandleArenaTeamAcceptOpcode(WorldPacket & /*recv_data*/)
{
sLog.outDebug("CMSG_ARENA_TEAM_ACCEPT"); // empty opcode
ArenaTeam *at = objmgr.GetArenaTeamById(_player->GetArenaTeamIdInvited());
if (!at)
return;
if (_player->GetArenaTeamId(at->GetSlot()))
{
// already in arena team that size
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ALREADY_IN_ARENA_TEAM);
return;
}
if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && _player->GetTeam() != objmgr.GetPlayerTeamByGUID(at->GetCaptain()))
{
// not let enemies sign petition
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_NOT_ALLIED);
return;
}
if (!at->AddMember(_player->GetGUID()))
{
// arena team not found
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S,"","",ERR_ARENA_TEAM_INTERNAL);
return;
}
// event
at->BroadcastEvent(ERR_ARENA_TEAM_JOIN_SS, _player->GetGUID(), 2, _player->GetName(), at->GetName(), "");
}
void WorldSession::HandleArenaTeamDeclineOpcode(WorldPacket & /*recv_data*/)
{
sLog.outDebug("CMSG_ARENA_TEAM_DECLINE"); // empty opcode
_player->SetArenaTeamIdInvited(0); // no more invited
}
void WorldSession::HandleArenaTeamLeaveOpcode(WorldPacket & recv_data)
{
sLog.outDebug("CMSG_ARENA_TEAM_LEAVE");
uint32 ArenaTeamId; // arena team id
recv_data >> ArenaTeamId;
ArenaTeam *at = objmgr.GetArenaTeamById(ArenaTeamId);
if (!at)
return;
if (_player->GetGUID() == at->GetCaptain() && at->GetMembersSize() > 1)
{
// check for correctness
SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, "", "", ERR_ARENA_TEAM_LEADER_LEAVE_S);
return;
}
// arena team has only one member (=captain)
if (_player->GetGUID() == at->GetCaptain())
{
at->Disband(this);
delete at;
return;
}
at->DelMember(_player->GetGUID());
// event
at->BroadcastEvent(ERR_ARENA_TEAM_LEAVE_SS, _player->GetGUID(), 2, _player->GetName(), at->GetName(), "");
// send you are no longer member of team
SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, at->GetName(), "", 0);
}
void WorldSession::HandleArenaTeamDisbandOpcode(WorldPacket & recv_data)
{
sLog.outDebug("CMSG_ARENA_TEAM_DISBAND");
uint32 ArenaTeamId; // arena team id
recv_data >> ArenaTeamId;
if (ArenaTeam *at = objmgr.GetArenaTeamById(ArenaTeamId))
{
if (at->GetCaptain() != _player->GetGUID())
return;
if (at->IsFighting())
return;
at->Disband(this);
delete at;
}
}
void WorldSession::HandleArenaTeamRemoveOpcode(WorldPacket & recv_data)
{
sLog.outDebug("CMSG_ARENA_TEAM_REMOVE");
uint32 ArenaTeamId;
std::string name;
recv_data >> ArenaTeamId;
recv_data >> name;
ArenaTeam *at = objmgr.GetArenaTeamById(ArenaTeamId);
if (!at) // arena team not found
return;
if (at->GetCaptain() != _player->GetGUID())
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PERMISSIONS);
return;
}
if (!normalizePlayerName(name))
return;
ArenaTeamMember* member = at->GetMember(name);
if (!member) // member not found
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", name, ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S);
return;
}
if (at->GetCaptain() == member->guid)
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, "", "", ERR_ARENA_TEAM_LEADER_LEAVE_S);
return;
}
at->DelMember(member->guid);
// event
at->BroadcastEvent(ERR_ARENA_TEAM_REMOVE_SSS, 0, 3, name, at->GetName(), _player->GetName());
}
void WorldSession::HandleArenaTeamLeaderOpcode(WorldPacket & recv_data)
{
sLog.outDebug("CMSG_ARENA_TEAM_LEADER");
uint32 ArenaTeamId;
std::string name;
recv_data >> ArenaTeamId;
recv_data >> name;
ArenaTeam *at = objmgr.GetArenaTeamById(ArenaTeamId);
if (!at) // arena team not found
return;
if (at->GetCaptain() != _player->GetGUID())
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PERMISSIONS);
return;
}
if (!normalizePlayerName(name))
return;
ArenaTeamMember* member = at->GetMember(name);
if (!member) // member not found
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", name, ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S);
return;
}
if (at->GetCaptain() == member->guid) // target player already captain
return;
at->SetCaptain(member->guid);
// event
at->BroadcastEvent(ERR_ARENA_TEAM_LEADER_CHANGED_SSS, 0, 3, _player->GetName(), name, at->GetName());
}
void WorldSession::SendArenaTeamCommandResult(uint32 team_action, const std::string& team, const std::string& player, uint32 error_id)
{
WorldPacket data(SMSG_ARENA_TEAM_COMMAND_RESULT, 4+team.length()+1+player.length()+1+4);
data << uint32(team_action);
data << team;
data << player;
data << uint32(error_id);
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"
*/
@@ -0,0 +1,664 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 Trinity <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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "ObjectMgr.h"
#include "Player.h"
#include "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "AuctionHouseBot.h"
#include "AuctionHouseMgr.h"
#include "Log.h"
#include "Opcodes.h"
#include "UpdateMask.h"
#include "Util.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("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.getConfig(CONFIG_AUCTION_LEVEL_REQ))
{
SendNotification(GetTrinityString(LANG_AUCTION_REQ), sWorld.getConfig(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, (7*4));
data << auction->Id;
data << auction->bid;
data << (uint32) 0; //unk
data << (uint32) 0; //unk
data << (uint32) 0; //unk
data << auction->item_template;
data << (uint32) 0; //unk
SendPacket(&data);
}
//this function sends mail to old bidder
void WorldSession::SendAuctionOutbiddedMail(AuctionEntry *auction, uint32 newPrice)
{
uint64 oldBidder_guid = MAKE_NEW_GUID(auction->bidder,0, HIGHGUID_PLAYER);
Player *oldBidder = objmgr.GetPlayer(oldBidder_guid);
uint32 oldBidder_accId = 0;
if (!oldBidder)
oldBidder_accId = objmgr.GetPlayerAccountIdByGUID(oldBidder_guid);
// old bidder exist
if (oldBidder || oldBidder_accId)
{
std::ostringstream msgAuctionOutbiddedSubject;
msgAuctionOutbiddedSubject << auction->item_template << ":0:" << AUCTION_OUTBIDDED << ":0:0";
if (oldBidder && !_player)
oldBidder->GetSession()->SendAuctionBidderNotification(auction->GetHouseId(), auction->Id, auctionbot.GetAHBplayerGUID(), newPrice, auction->GetAuctionOutBid(), auction->item_template);
if (oldBidder && _player)
oldBidder->GetSession()->SendAuctionBidderNotification(auction->GetHouseId(), auction->Id, _player->GetGUID(), newPrice, auction->GetAuctionOutBid(), auction->item_template);
MailDraft(msgAuctionOutbiddedSubject.str(), "") // TODO: fix body
.AddMoney(auction->bid)
.SendMailTo(MailReceiver(oldBidder, auction->bidder), auction, MAIL_CHECK_MASK_COPIED);
}
}
//this function sends mail, when auction is cancelled to old bidder
void WorldSession::SendAuctionCancelledToBidderMail(AuctionEntry* auction)
{
uint64 bidder_guid = MAKE_NEW_GUID(auction->bidder, 0, HIGHGUID_PLAYER);
Player *bidder = objmgr.GetPlayer(bidder_guid);
uint32 bidder_accId = 0;
if (!bidder)
bidder_accId = objmgr.GetPlayerAccountIdByGUID(bidder_guid);
// bidder exist
if (bidder || bidder_accId)
{
std::ostringstream msgAuctionCancelledSubject;
msgAuctionCancelledSubject << auction->item_template << ":0:" << AUCTION_CANCELLED_TO_BIDDER << ":0:0";
MailDraft(msgAuctionCancelledSubject.str(), "") // TODO: fix body
.AddMoney(auction->bid)
.SendMailTo(MailReceiver(bidder, auction->bidder), auction, MAIL_CHECK_MASK_COPIED);
}
}
//this void creates new auction and adds auction to some auctionhouse
void WorldSession::HandleAuctionSellItem(WorldPacket & recv_data)
{
uint64 auctioneer, item;
uint32 etime, bid, buyout;
recv_data >> auctioneer;
recv_data.read_skip<uint32>(); // const 1?
recv_data >> item;
recv_data.read_skip<uint32>(); // unk 3.2.2, const 1?
recv_data >> bid;
recv_data >> buyout;
recv_data >> etime;
Player *pl = GetPlayer();
if (!item || !bid || !etime)
return; //check for cheaters
Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(auctioneer,UNIT_NPC_FLAG_AUCTIONEER);
if (!pCreature)
{
sLog.outDebug("WORLD: HandleAuctionSellItem - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(auctioneer)));
return;
}
AuctionHouseEntry const* auctionHouseEntry = AuctionHouseMgr::GetAuctionHouseEntry(pCreature->getFaction());
if (!auctionHouseEntry)
{
sLog.outDebug("WORLD: HandleAuctionSellItem - Unit (GUID: %u) has wrong faction.", uint32(GUID_LOPART(auctioneer)));
return;
}
sLog.outDebug("WORLD: HandleAuctionSellItem - ETIME: %u", etime);
// client send time in minutes, convert to common used sec time
etime *= MINUTE;
sLog.outDebug("WORLD: HandleAuctionSellItem - ETIME: %u", etime);
// 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 = pl->GetItemByGuid(item);
//do not allow to sell already auctioned items
if (auctionmgr.GetAItem(GUID_LOPART(item)))
{
sLog.outError("AuctionError, player %s is sending item id: %u, but item is already in another auction", pl->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->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_CONJURED) || it->GetUInt32Value(ITEM_FIELD_DURATION))
{
SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR);
return;
}
AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap(pCreature->getFaction());
//we have to take deposit :
uint32 deposit = auctionmgr.GetAuctionDeposit(auctionHouseEntry, etime, it);
if (pl->GetMoney() < deposit)
{
SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_NOT_ENOUGHT_MONEY);
return;
}
if (GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE))
{
sLog.outCommand(GetAccountId(),"GM %s (Account: %u) create auction: %s (Entry: %u Count: %u)",
GetPlayerName(),GetAccountId(),it->GetProto()->Name1,it->GetEntry(),it->GetCount());
}
pl->ModifyMoney(-int32(deposit));
uint32 auction_time = uint32(etime * sWorld.getRate(RATE_AUCTION_TIME));
AuctionEntry *AH = new AuctionEntry;
AH->Id = objmgr.GenerateAuctionID();
if (sWorld.getConfig(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 = pl->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());
auctionmgr.AddAItem(it);
auctionHouse->AddAuction(AH);
pl->MoveItemFromInventory(it->GetBagSlot(), it->GetSlot(), true);
CharacterDatabase.BeginTransaction();
it->DeleteFromInventoryDB();
it->SaveToDB(); // recursive and not have transaction guard into self, not in inventiory and can be save standalone
AH->SaveToDB();
pl->SaveInventoryAndGoldToDB();
CharacterDatabase.CommitTransaction();
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)
{
uint64 auctioneer;
uint32 auctionId;
uint32 price;
recv_data >> auctioneer;
recv_data >> auctionId >> price;
if (!auctionId || !price)
return; //check for cheaters
Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(auctioneer, UNIT_NPC_FLAG_AUCTIONEER);
if (!pCreature)
{
sLog.outDebug("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 = auctionmgr.GetAuctionsMap(pCreature->getFaction());
AuctionEntry *auction = auctionHouse->GetAuction(auctionId);
Player *pl = GetPlayer();
if (!auction || auction->owner == pl->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 = objmgr.GetPlayer(MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER));
if (!auction_owner && objmgr.GetPlayerAccountIdByGUID(MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER)) == pl->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 (price > pl->GetMoney())
{
//you don't have enought money!, client tests!
//SendAuctionCommandResult(auction->auctionId, AUCTION_PLACE_BID, ???);
return;
}
if (price < auction->buyout || auction->buyout == 0)
{
if (auction->bidder > 0)
{
if (auction->bidder == pl->GetGUIDLow())
pl->ModifyMoney(-int32(price - auction->bid));
else
{
// mail to last bidder and return money
SendAuctionOutbiddedMail(auction, price);
pl->ModifyMoney(-int32(price));
}
}
else
pl->ModifyMoney(-int32(price));
auction->bidder = pl->GetGUIDLow();
auction->bid = price;
GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID, price);
// after this update we should save player's money ...
CharacterDatabase.BeginTransaction();
CharacterDatabase.PExecute("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 (pl->GetGUIDLow() == auction->bidder)
pl->ModifyMoney(-int32(auction->buyout - auction->bid));
else
{
pl->ModifyMoney(-int32(auction->buyout));
if (auction->bidder) //buyout for bidded auction ..
SendAuctionOutbiddedMail(auction, auction->buyout);
}
auction->bidder = pl->GetGUIDLow();
auction->bid = auction->buyout;
GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID, auction->buyout);
auctionmgr.SendAuctionSalePendingMail(auction);
auctionmgr.SendAuctionSuccessfulMail(auction);
auctionmgr.SendAuctionWonMail(auction);
SendAuctionCommandResult(auction->Id, AUCTION_PLACE_BID, AUCTION_OK);
CharacterDatabase.BeginTransaction();
auction->DeleteFromDB();
uint32 item_template = auction->item_template;
auctionmgr.RemoveAItem(auction->item_guidlow);
auctionHouse->RemoveAuction(auction, item_template);
}
pl->SaveInventoryAndGoldToDB();
CharacterDatabase.CommitTransaction();
}
//this void is called when auction_owner cancels his auction
void WorldSession::HandleAuctionRemoveItem(WorldPacket & recv_data)
{
uint64 auctioneer;
uint32 auctionId;
recv_data >> auctioneer;
recv_data >> auctionId;
//sLog.outDebug("Cancel AUCTION AuctionID: %u", auctionId);
Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(auctioneer,UNIT_NPC_FLAG_AUCTIONEER);
if (!pCreature)
{
sLog.outDebug("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 = auctionmgr.GetAuctionsMap(pCreature->getFaction());
AuctionEntry *auction = auctionHouse->GetAuction(auctionId);
Player *pl = GetPlayer();
if (auction && auction->owner == pl->GetGUIDLow())
{
Item *pItem = auctionmgr.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 (pl->GetMoney() < auctionCut) //player doesn't have enough money, maybe message needed
return;
//some auctionBidderNotification would be needed, but don't know that parts..
SendAuctionCancelledToBidderMail(auction);
pl->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(pl, 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", pl->GetGUIDLow(), auctionId);
return;
}
//inform player, that auction is removed
SendAuctionCommandResult(auction->Id, AUCTION_CANCEL, AUCTION_OK);
// Now remove the auction
CharacterDatabase.BeginTransaction();
pl->SaveInventoryAndGoldToDB();
auction->DeleteFromDB();
uint32 item_template = auction->item_template;
auctionmgr.RemoveAItem(auction->item_guidlow);
auctionHouse->RemoveAuction(auction, item_template);
CharacterDatabase.CommitTransaction();
}
//called when player lists his bids
void WorldSession::HandleAuctionListBidderItems(WorldPacket & recv_data)
{
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 *pCreature = GetPlayer()->GetNPCIfCanInteractWith(guid,UNIT_NPC_FLAG_AUCTIONEER);
if (!pCreature)
{
sLog.outDebug("WORLD: HandleAuctionListBidderItems - 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 = auctionmgr.GetAuctionsMap(pCreature->getFaction());
WorldPacket data(SMSG_AUCTION_BIDDER_LIST_RESULT, (4+4+4));
Player *pl = 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,pl,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)
{
uint32 listfrom;
uint64 guid;
recv_data >> guid;
recv_data >> listfrom; // not used in fact (this list not have page control in client)
Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(guid,UNIT_NPC_FLAG_AUCTIONEER);
if (!pCreature)
{
sLog.outDebug("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 = auctionmgr.GetAuctionsMap(pCreature->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)
{
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(16); // unknown 16 bytes: 00 07 01 00 00 01 05 00 06 00 09 01 08 00 03 00
Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(guid,UNIT_NPC_FLAG_AUCTIONEER);
if (!pCreature)
{
sLog.outDebug("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 = auctionmgr.GetAuctionsMap(pCreature->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("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);
}
@@ -0,0 +1,803 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 Trinity <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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "Common.h"
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "ArenaTeam.h"
#include "BattleGroundMgr.h"
#include "BattleGroundWS.h"
#include "BattleGround.h"
#include "Chat.h"
#include "Language.h"
#include "Log.h"
#include "Player.h"
#include "Object.h"
#include "Opcodes.h"
void WorldSession::HandleBattlemasterHelloOpcode(WorldPacket & recv_data)
{
uint64 guid;
recv_data >> guid;
sLog.outDebug("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;
}
BattleGroundTypeId bgTypeId = BattleGroundTypeId(bgTypeId_);
sLog.outDebug("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)
{
// 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);
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("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("Battleground: the following players are joining as group:");
ginfo = bgQueue.AddGroup(_player, grp, bgTypeId, bracketEntry, 0, false, isPremade, 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("Battleground: player joined queue for bg queue type %u bg type %u: GUID %u, NAME %s",bgQueueTypeId,bgTypeId,member->GetGUIDLow(), member->GetName());
}
sLog.outDebug("Battleground: group end");
}
sBattleGroundMgr.ScheduleQueueUpdate(0, 0, bgQueueTypeId, bgTypeId, bracketEntry->GetBracketId());
}
void WorldSession::HandleBattleGroundPlayerPositionsOpcode(WorldPacket & /*recv_data*/)
{
// empty opcode
sLog.outDebug("WORLD: Recvd MSG_BATTLEGROUND_PLAYER_POSITIONS Message");
BattleGround *bg = _player->GetBattleGround();
if (!bg) // can't be received if player not in battleground
return;
switch(bg->GetTypeID(true))
{
case BATTLEGROUND_WS:
{
uint32 count1 = 0; //always constant zero?
uint32 count2 = 0; //count of next fields
Player *ali_plr = objmgr.GetPlayer(((BattleGroundWS*)bg)->GetAllianceFlagPickerGUID());
if (ali_plr)
++count2;
Player *horde_plr = objmgr.GetPlayer(((BattleGroundWS*)bg)->GetHordeFlagPickerGUID());
if (horde_plr)
++count2;
WorldPacket data(MSG_BATTLEGROUND_PLAYER_POSITIONS, (4+4+16*count1+16*count2));
data << count1; // alliance flag holders count - obsolete, now always 0
/*for (uint8 i = 0; i < count1; ++i)
{
data << uint64(0); // guid
data << (float)0; // x
data << (float)0; // y
}*/
data << count2; // horde flag holders count - obsolete, now count of next fields
if (ali_plr)
{
data << (uint64)ali_plr->GetGUID();
data << (float)ali_plr->GetPositionX();
data << (float)ali_plr->GetPositionY();
}
if (horde_plr)
{
data << (uint64)horde_plr->GetGUID();
data << (float)horde_plr->GetPositionX();
data << (float)horde_plr->GetPositionY();
}
SendPacket(&data);
}
break;
case BATTLEGROUND_EY:
//TODO : fix me!
break;
case BATTLEGROUND_AB:
case BATTLEGROUND_AV:
{
//for other BG types - send default
WorldPacket data(MSG_BATTLEGROUND_PLAYER_POSITIONS, (4+4));
data << uint32(0);
data << uint32(0);
SendPacket(&data);
}
break;
default:
//maybe it is sent also in arena - do nothing
break;
}
}
void WorldSession::HandlePVPLogDataOpcode(WorldPacket & /*recv_data*/)
{
sLog.outDebug("WORLD: Recvd MSG_PVP_LOG_DATA Message");
BattleGround *bg = _player->GetBattleGround();
if (!bg)
return;
WorldPacket data;
sBattleGroundMgr.BuildPvpLogDataPacket(&data, bg);
SendPacket(&data);
sLog.outDebug("WORLD: Sent MSG_PVP_LOG_DATA Message");
}
void WorldSession::HandleBattlefieldListOpcode(WorldPacket &recv_data)
{
sLog.outDebug("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.outError("Battleground: invalid bgtype received.");
return;
}
WorldPacket data;
sBattleGroundMgr.BuildBattleGroundListPacket(&data, 0, _player, BattleGroundTypeId(bgTypeId), fromWhere);
SendPacket(&data);
}
void WorldSession::HandleBattleFieldPortOpcode(WorldPacket &recv_data)
{
sLog.outDebug("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.outError("BattlegroundHandler: invalid bgtype (%u) received.", bgTypeId_);
return;
}
if (!_player->InBattleGroundQueue())
{
sLog.outError("BattlegroundHandler: Invalid CMSG_BATTLEFIELD_PORT received from player (%u), he is not in bg_queue.", _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("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("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)
{
ArenaTeam * at = objmgr.GetArenaTeamById(ginfo.Team);
if (at)
{
sLog.outDebug("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.OpponentsTeamRating);
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.ArenaTeamRating, ginfo.ArenaType, bgQueueTypeId, bgTypeId, bracketEntry->GetBracketId());
SendPacket(&data);
sLog.outDebug("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("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("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("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("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("WORLD: CMSG_BATTLEMASTER_JOIN_ARENA");
//recv_data.hexlike();
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;
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;
}
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 = objmgr.GetArenaTeamById(ateamId);
if (!at)
{
_player->GetSession()->SendNotInArenaTeamPacket(arenatype);
return;
}
// get the team rating for queueing
arenaRating = at->GetRating();
// the arenateam id must match for everyone in the group
// get the personal ratings for queueing
uint32 avg_pers_rating = 0;
for (GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next())
{
Player *member = itr->getSource();
// calc avg personal rating
avg_pers_rating += member->GetArenaPersonalRating(arenaslot);
}
if (arenatype)
avg_pers_rating /= arenatype;
// if avg personal rating is more than 150 points below the teams rating, the team will be queued against an opponent matching or similar to the average personal rating
if (avg_pers_rating + 150 < arenaRating)
arenaRating = avg_pers_rating;
if (arenaRating <= 0)
arenaRating = 1;
}
BattleGroundQueue &bgQueue = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId];
if (asGroup)
{
uint32 avgTime = 0;
if (err > 0)
{
sLog.outDebug("Battleground: arena join as group start");
if (isRated)
{
sLog.outDebug("Battleground: arena team id %u, leader %s queued with rating %u for type %u",_player->GetArenaTeamId(arenaslot),_player->GetName(),arenaRating,arenatype);
bg->SetRated(true);
}
else
bg->SetRated(false);
GroupQueueInfo * ginfo = bgQueue.AddGroup(_player, grp, bgTypeId, bracketEntry, arenatype, isRated, false, arenaRating, 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("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, 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("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(arenaRating, arenatype, bgQueueTypeId, bgTypeId, bracketEntry->GetBracketId());
}
void WorldSession::HandleReportPvPAFK(WorldPacket & recv_data)
{
uint64 playerGuid;
recv_data >> playerGuid;
Player *reportedPlayer = objmgr.GetPlayer(playerGuid);
if (!reportedPlayer)
{
sLog.outDebug("WorldSession::HandleReportPvPAFK: player not found");
return;
}
sLog.outDebug("WorldSession::HandleReportPvPAFK: %s reported %s", _player->GetName(), reportedPlayer->GetName());
reportedPlayer->ReportedAfkBy(_player);
}
@@ -0,0 +1,318 @@
/*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#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*/)
{
sLog.outDebug("WORLD: CMSG_CALENDAR_GET_CALENDAR"); // empty
time_t cur_time = time(NULL);
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(0); // unk
data << uint32(secsToTimeBitFields(cur_time)); // current time
uint32 counter = 0;
size_t p_counter = data.wpos();
data << uint32(counter); // instance save count
for (int 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 *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 12:00)
counter = 0;
p_counter = data.wpos();
data << uint32(counter); // raid reset count
ResetTimeByMapDifficultyMap const& resets = sInstanceSaveManager.GetResetTimeMap();
for (ResetTimeByMapDifficultyMap::const_iterator itr = resets.begin(); itr != resets.end(); ++itr)
{
uint32 mapid = PAIR32_LOPART(itr->first);
MapEntry const* mapEnt = sMapStore.LookupEntry(mapid);
if (!mapEnt || !mapEnt->IsRaid())
continue;
data << uint32(mapid);
data << uint32(itr->second - cur_time);
data << uint32(mapEnt->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
}
*/
sLog.outDebug("Sending calendar");
data.hexlike();
SendPacket(&data);
}
void WorldSession::HandleCalendarGetEvent(WorldPacket &recv_data)
{
sLog.outDebug("WORLD: CMSG_CALENDAR_GET_EVENT");
recv_data.hexlike();
recv_data.read_skip<uint64>(); // unk
}
void WorldSession::HandleCalendarGuildFilter(WorldPacket &recv_data)
{
sLog.outDebug("WORLD: CMSG_CALENDAR_GUILD_FILTER");
recv_data.hexlike();
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("WORLD: CMSG_CALENDAR_ARENA_TEAM");
recv_data.hexlike();
recv_data.read_skip<uint32>(); // unk
}
void WorldSession::HandleCalendarAddEvent(WorldPacket &recv_data)
{
sLog.outDebug("WORLD: CMSG_CALENDAR_ADD_EVENT");
recv_data.hexlike();
recv_data.rpos(recv_data.wpos()); // 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("WORLD: CMSG_CALENDAR_UPDATE_EVENT");
recv_data.hexlike();
recv_data.rpos(recv_data.wpos()); // 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("WORLD: CMSG_CALENDAR_REMOVE_EVENT");
recv_data.hexlike();
recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam
//recv_data >> uint64
//recv_data >> uint64
//recv_data >> uint32
}
void WorldSession::HandleCalendarCopyEvent(WorldPacket &recv_data)
{
sLog.outDebug("WORLD: CMSG_CALENDAR_COPY_EVENT");
recv_data.hexlike();
recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam
//recv_data >> uint64
//recv_data >> uint64
//recv_data >> uint32
}
void WorldSession::HandleCalendarEventInvite(WorldPacket &recv_data)
{
sLog.outDebug("WORLD: CMSG_CALENDAR_EVENT_INVITE");
recv_data.hexlike();
recv_data.rpos(recv_data.wpos()); // 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("WORLD: CMSG_CALENDAR_EVENT_RSVP");
recv_data.hexlike();
recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam
//recv_data >> uint64
//recv_data >> uint64
//recv_data >> uint32
}
void WorldSession::HandleCalendarEventRemoveInvite(WorldPacket &recv_data)
{
sLog.outDebug("WORLD: CMSG_CALENDAR_EVENT_REMOVE_INVITE");
recv_data.hexlike();
recv_data.rpos(recv_data.wpos()); // 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("WORLD: CMSG_CALENDAR_EVENT_STATUS");
recv_data.hexlike();
recv_data.rpos(recv_data.wpos()); // 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("WORLD: CMSG_CALENDAR_EVENT_MODERATOR_STATUS");
recv_data.hexlike();
recv_data.rpos(recv_data.wpos()); // 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("WORLD: CMSG_CALENDAR_COMPLAIN");
recv_data.hexlike();
recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam
//recv_data >> uint64
//recv_data >> uint64
//recv_data >> uint64
}
void WorldSession::HandleCalendarGetNumPending(WorldPacket & /*recv_data*/)
{
sLog.outDebug("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);
}
@@ -0,0 +1,323 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 Trinity <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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "Policies/SingletonImp.h"
#include "ObjectMgr.h" // for normalizePlayerName
#include "ChannelMgr.h"
void WorldSession::HandleJoinChannel(WorldPacket& recvPacket)
{
sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
uint32 channel_id;
uint8 unknown1, unknown2;
std::string channelname, pass;
recvPacket >> channel_id >> unknown1 >> unknown2;
recvPacket >> channelname;
if (channelname.empty())
return;
recvPacket >> pass;
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("Opcode %u", recvPacket.GetOpcode());
//recvPacket.hexlike();
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("Opcode %u", recvPacket.GetOpcode());
//recvPacket.hexlike();
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("Opcode %u", recvPacket.GetOpcode());
//recvPacket.hexlike();
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("Opcode %u", recvPacket.GetOpcode());
//recvPacket.hexlike();
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("Opcode %u", recvPacket.GetOpcode());
//recvPacket.hexlike();
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("Opcode %u", recvPacket.GetOpcode());
//recvPacket.hexlike();
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("Opcode %u", recvPacket.GetOpcode());
//recvPacket.hexlike();
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("Opcode %u", recvPacket.GetOpcode());
//recvPacket.hexlike();
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("Opcode %u", recvPacket.GetOpcode());
//recvPacket.hexlike();
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("Opcode %u", recvPacket.GetOpcode());
//recvPacket.hexlike();
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("Opcode %u", recvPacket.GetOpcode());
//recvPacket.hexlike();
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("Opcode %u", recvPacket.GetOpcode());
//recvPacket.hexlike();
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("Opcode %u", recvPacket.GetOpcode());
//recvPacket.hexlike();
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("Opcode %u", recvPacket.GetOpcode());
//recvPacket.hexlike();
std::string channelname;
recvPacket >> channelname;
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
if (Channel *chn = cMgr->GetChannel(channelname, _player))
chn->Announce(_player->GetGUID());
}
void WorldSession::HandleChannelModerate(WorldPacket& recvPacket)
{
sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
//recvPacket.hexlike();
std::string channelname;
recvPacket >> channelname;
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
if (Channel *chn = cMgr->GetChannel(channelname, _player))
chn->Moderate(_player->GetGUID());
}
void WorldSession::HandleChannelDisplayListQuery(WorldPacket &recvPacket)
{
sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
//recvPacket.hexlike();
std::string channelname;
recvPacket >> channelname;
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
if (Channel *chn = cMgr->GetChannel(channelname, _player))
chn->List(_player);
}
void WorldSession::HandleGetChannelMemberCount(WorldPacket &recvPacket)
{
sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
//recvPacket.hexlike();
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("Opcode %u", recvPacket.GetOpcode());
//recvPacket.hexlike();
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
@@ -0,0 +1,756 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 Trinity <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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "Common.h"
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "Database/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"
bool WorldSession::processChatmessageFurtherAfterSecurityChecks(std::string& msg, uint32 lang)
{
if (lang != LANG_ADDON)
{
// strip invisible characters for non-addon messages
if (sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
stripLineInvisibleChars(msg);
if (sWorld.getConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY) && GetSecurity() < SEC_MODERATOR
&& !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.getConfig(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);
return;
}
//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);
return;
}
if (langDesc->skill_id != 0 && !_player->HasSkill(langDesc->skill_id))
{
// also check SPELL_AURA_COMPREHEND_LANGUAGE (client offers option to speak in that language)
Unit::AuraEffectList const& langAuras = _player->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);
return;
}
}
if (lang == LANG_ADDON)
{
if (sWorld.getConfig(CONFIG_CHATLOG_ADDON))
{
std::string msg = "";
recv_data >> msg;
if (msg.empty())
{
sLog.outDebug("Player %s send empty addon msg", GetPlayer()->GetName());
return;
}
sLog.outChat("[ADDON] Player %s sends: %s",
GetPlayer()->GetName(), msg.c_str());
}
// Disabled addon channel?
if (!sWorld.getConfig(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 (_player->isGameMaster())
lang = LANG_UNIVERSAL;
else
{
// send in universal language in two side iteration allowed mode
if (sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT))
lang = LANG_UNIVERSAL;
else
{
switch(type)
{
case CHAT_MSG_PARTY:
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.getConfig(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.getConfig(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 = _player->GetAuraEffectsByType(SPELL_AURA_MOD_LANGUAGE);
if (!ModLangAuras.empty())
lang = ModLangAuras.front()->GetMiscValue();
}
if (!_player->CanSpeak())
{
std::string timeStr = secsToTimeString(m_muteTime - time(NULL));
SendNotification(GetTrinityString(LANG_WAIT_BEFORE_SPEAKING),timeStr.c_str());
return;
}
if (type != CHAT_MSG_AFK && type != CHAT_MSG_DND)
GetPlayer()->UpdateSpeakTime();
}
if (GetPlayer()->HasAura(1852) && type != CHAT_MSG_WHISPER)
{
std::string msg="";
recv_data >> msg;
SendNotification(GetTrinityString(LANG_GM_SILENCE), GetPlayer()->GetName());
return;
}
switch(type)
{
case CHAT_MSG_SAY:
case CHAT_MSG_EMOTE:
case CHAT_MSG_YELL:
{
std::string msg;
recv_data >> msg;
if (msg.empty())
break;
if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
break;
if (_player->getLevel() < sWorld.getConfig(CONFIG_CHAT_SAY_LEVEL_REQ))
{
SendNotification(GetTrinityString(LANG_SAY_REQ), sWorld.getConfig(CONFIG_CHAT_SAY_LEVEL_REQ));
return;
}
if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
return;
if (msg.empty())
break;
if (type == CHAT_MSG_SAY)
GetPlayer()->Say(msg, lang);
else if (type == CHAT_MSG_EMOTE)
GetPlayer()->TextEmote(msg);
else if (type == CHAT_MSG_YELL)
GetPlayer()->Yell(msg, lang);
} break;
case CHAT_MSG_WHISPER:
{
std::string to, msg;
recv_data >> to;
recv_data >> msg;
if (_player->getLevel() < sWorld.getConfig(CONFIG_CHAT_WHISPER_LEVEL_REQ))
{
SendNotification(GetTrinityString(LANG_WHISPER_REQ), sWorld.getConfig(CONFIG_CHAT_WHISPER_LEVEL_REQ));
return;
}
if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
return;
if (msg.empty())
break;
if (!normalizePlayerName(to))
{
SendPlayerNotFoundNotice(to);
break;
}
Player *player = objmgr.GetPlayer(to.c_str());
uint32 tSecurity = GetSecurity();
uint32 pSecurity = player ? player->GetSession()->GetSecurity() : SEC_PLAYER;
if (!player || (tSecurity == SEC_PLAYER && pSecurity > SEC_PLAYER && !player->isAcceptWhispers()))
{
SendPlayerNotFoundNotice(to);
return;
}
if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT) && tSecurity == SEC_PLAYER && pSecurity == SEC_PLAYER)
{
uint32 sidea = GetPlayer()->GetTeam();
uint32 sideb = player->GetTeam();
if (sidea != sideb)
{
SendWrongFactionNotice();
return;
}
}
if (GetPlayer()->HasAura(1852) && !player->isGameMaster())
{
SendNotification(GetTrinityString(LANG_GM_SILENCE), GetPlayer()->GetName());
return;
}
GetPlayer()->Whisper(msg, lang, player->GetGUID());
} break;
case CHAT_MSG_PARTY:
case CHAT_MSG_PARTY_LEADER:
{
std::string msg;
recv_data >> msg;
if (msg.empty())
break;
if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
break;
if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
return;
if (msg.empty())
break;
// 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;
WorldPacket data;
ChatHandler::FillMessageData(&data, this, type, lang, NULL, 0, msg.c_str(), NULL);
group->BroadcastPacket(&data, false, group->GetMemberGroup(GetPlayer()->GetGUID()));
if (sWorld.getConfig(CONFIG_CHATLOG_PARTY))
sLog.outChat("[PARTY] Player %s tells group with leader %s: %s",
GetPlayer()->GetName(), group->GetLeaderName(), msg.c_str());
} break;
case CHAT_MSG_GUILD:
{
std::string msg;
recv_data >> msg;
if (msg.empty())
break;
if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
break;
if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
return;
if (msg.empty())
break;
if (GetPlayer()->GetGuildId())
{
Guild *guild = objmgr.GetGuildById(GetPlayer()->GetGuildId());
if (guild)
guild->BroadcastToGuild(this, msg, lang == LANG_ADDON ? LANG_ADDON : LANG_UNIVERSAL);
if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHATLOG_GUILD))
{
sLog.outChat("[GUILD] Player %s tells guild %s: %s",
GetPlayer()->GetName(), guild->GetName().c_str(), msg.c_str());
}
else if (lang == LANG_ADDON && sWorld.getConfig(CONFIG_CHATLOG_ADDON))
{
sLog.outChat("[ADDON] Player %s sends to guild %s: %s",
GetPlayer()->GetName(), guild->GetName().c_str(), msg.c_str());
}
}
break;
}
case CHAT_MSG_OFFICER:
{
std::string msg;
recv_data >> msg;
if (msg.empty())
break;
if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
break;
if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
return;
if (msg.empty())
break;
if (GetPlayer()->GetGuildId())
{
Guild *guild = objmgr.GetGuildById(GetPlayer()->GetGuildId());
if (guild)
guild->BroadcastToOfficers(this, msg, lang == LANG_ADDON ? LANG_ADDON : LANG_UNIVERSAL);
if (sWorld.getConfig(CONFIG_CHATLOG_GUILD))
sLog.outChat("[OFFICER] Player %s tells guild %s officers: %s",
GetPlayer()->GetName(), guild->GetName().c_str(), msg.c_str());
}
break;
}
case CHAT_MSG_RAID:
{
std::string msg;
recv_data >> msg;
if (msg.empty())
break;
if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
break;
if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
return;
if (msg.empty())
break;
// 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;
}
WorldPacket data;
ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID, lang, "", 0, msg.c_str(), NULL);
group->BroadcastPacket(&data, false);
if (sWorld.getConfig(CONFIG_CHATLOG_RAID))
sLog.outChat("[RAID] Player %s tells raid with leader %s: %s",
GetPlayer()->GetName(), group->GetLeaderName(), msg.c_str());
} break;
case CHAT_MSG_RAID_LEADER:
{
std::string msg;
recv_data >> msg;
if (msg.empty())
break;
if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
break;
if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
return;
if (msg.empty())
break;
// 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;
}
WorldPacket data;
ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID_LEADER, lang, "", 0, msg.c_str(), NULL);
group->BroadcastPacket(&data, false);
if (sWorld.getConfig(CONFIG_CHATLOG_RAID))
sLog.outChat("[RAID] Leader player %s tells raid: %s",
GetPlayer()->GetName(), msg.c_str());
} break;
case CHAT_MSG_RAID_WARNING:
{
std::string msg;
recv_data >> msg;
if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
return;
if (msg.empty())
break;
Group *group = GetPlayer()->GetGroup();
if (!group || !group->isRaidGroup() || !(group->IsLeader(GetPlayer()->GetGUID()) || group->IsAssistant(GetPlayer()->GetGUID())) || group->isBGGroup())
return;
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);
if (sWorld.getConfig(CONFIG_CHATLOG_RAID))
sLog.outChat("[RAID] Leader player %s warns raid with: %s",
GetPlayer()->GetName(), msg.c_str());
} break;
case CHAT_MSG_BATTLEGROUND:
{
std::string msg;
recv_data >> msg;
if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
return;
if (msg.empty())
break;
//battleground raid is always in Player->GetGroup(), never in GetOriginalGroup()
Group *group = GetPlayer()->GetGroup();
if (!group || !group->isBGGroup())
return;
WorldPacket data;
ChatHandler::FillMessageData(&data, this, CHAT_MSG_BATTLEGROUND, lang, "", 0, msg.c_str(), NULL);
group->BroadcastPacket(&data, false);
if (sWorld.getConfig(CONFIG_CHATLOG_BGROUND))
sLog.outChat("[BATTLEGROUND] Player %s tells battleground with leader %s: %s",
GetPlayer()->GetName(), group->GetLeaderName(), msg.c_str());
} break;
case CHAT_MSG_BATTLEGROUND_LEADER:
{
std::string msg;
recv_data >> msg;
if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
return;
if (msg.empty())
break;
// battleground raid is always in Player->GetGroup(), never in GetOriginalGroup()
Group *group = GetPlayer()->GetGroup();
if (!group || !group->isBGGroup() || !group->IsLeader(GetPlayer()->GetGUID()))
return;
WorldPacket data;
ChatHandler::FillMessageData(&data, this, CHAT_MSG_BATTLEGROUND_LEADER, lang, "", 0, msg.c_str(), NULL);
group->BroadcastPacket(&data, false);
if (sWorld.getConfig(CONFIG_CHATLOG_BGROUND))
sLog.outChat("[RAID] Leader player %s tells battleground: %s",
GetPlayer()->GetName(), msg.c_str());
} break;
case CHAT_MSG_CHANNEL:
{
std::string channel, msg;
recv_data >> channel;
recv_data >> msg;
if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
return;
if (_player->getLevel() < sWorld.getConfig(CONFIG_CHAT_CHANNEL_LEVEL_REQ))
{
SendNotification(GetTrinityString(LANG_CHANNEL_REQ), sWorld.getConfig(CONFIG_CHAT_CHANNEL_LEVEL_REQ));
return;
}
if (msg.empty())
break;
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
{
if (Channel *chn = cMgr->GetChannel(channel, _player))
{
chn->Say(_player->GetGUID(), msg.c_str(), lang);
if ((chn->HasFlag(CHANNEL_FLAG_TRADE) ||
chn->HasFlag(CHANNEL_FLAG_GENERAL) ||
chn->HasFlag(CHANNEL_FLAG_CITY) ||
chn->HasFlag(CHANNEL_FLAG_LFG)) &&
sWorld.getConfig(CONFIG_CHATLOG_SYSCHAN))
sLog.outChat("[SYSCHAN] Player %s tells channel %s: %s",
GetPlayer()->GetName(), chn->GetName().c_str(), msg.c_str());
else if (sWorld.getConfig(CONFIG_CHATLOG_CHANNEL))
sLog.outChat("[CHANNEL] Player %s tells channel %s: %s",
GetPlayer()->GetName(), chn->GetName().c_str(), msg.c_str());
}
}
} break;
case CHAT_MSG_AFK:
{
std::string msg;
recv_data >> msg;
if ((msg.empty() || !_player->isAFK()) && !_player->isInCombat())
{
if (!_player->isAFK())
{
if (msg.empty())
msg = GetTrinityString(LANG_PLAYER_AFK_DEFAULT);
_player->afkMsg = msg;
}
_player->ToggleAFK();
if (_player->isAFK() && _player->isDND())
_player->ToggleDND();
}
} break;
case CHAT_MSG_DND:
{
std::string msg;
recv_data >> msg;
if (msg.empty() || !_player->isDND())
{
if (!_player->isDND())
{
if (msg.empty())
msg = GetTrinityString(LANG_PLAYER_DND_DEFAULT);
_player->dndMsg = 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())
return;
uint32 emote;
recv_data >> emote;
GetPlayer()->HandleEmoteCommand(emote);
}
namespace Trinity
{
class EmoteChatBuilder
{
public:
EmoteChatBuilder(Player const& pl, uint32 text_emote, uint32 emote_num, Unit const* target)
: i_player(pl), i_text_emote(text_emote), i_emote_num(emote_num), i_target(target) {}
void operator()(WorldPacket& data, int32 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;
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:
GetPlayer()->HandleEmoteCommand(emote_anim);
break;
}
Unit* unit = ObjectAccessor::GetUnit(*_player, guid);
CellPair p = Trinity::ComputeCellPair(GetPlayer()->GetPositionX(), GetPlayer()->GetPositionY());
Cell cell(p);
cell.data.Part.reserved = ALL_DISTRICT;
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.getConfig(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.getConfig(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("WORLD: Received CMSG_CHAT_IGNORED");
recv_data >> iguid;
recv_data >> unk; // probably related to spam reporting
Player *player = objmgr.GetPlayer(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("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);
}
@@ -0,0 +1,91 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 Trinity <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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "Common.h"
#include "Log.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "ObjectAccessor.h"
#include "CreatureAI.h"
#include "ObjectDefines.h"
void WorldSession::HandleAttackSwingOpcode(WorldPacket & recv_data)
{
uint64 guid;
recv_data >> guid;
DEBUG_LOG("WORLD: Recvd CMSG_ATTACKSWING Message guidlow:%u guidhigh:%u", GUID_LOPART(guid), GUID_HIPART(guid));
Unit *pEnemy = ObjectAccessor::GetUnit(*_player, guid);
if (!pEnemy)
{
if (!IS_UNIT_GUID(guid))
sLog.outError("WORLD: Object %u (TypeID: %u) isn't player, pet or creature",GUID_LOPART(guid),GuidHigh2TypeId(GUID_HIPART(guid)));
else
sLog.outError("WORLD: Enemy %s %u not found",GetLogNameForGuid(guid),GUID_LOPART(guid));
// stop attack state at client
SendAttackStop(NULL);
return;
}
if (!_player->canAttack(pEnemy))
{
sLog.outError("WORLD: Enemy %s %u is friendly",(IS_PLAYER_GUID(guid) ? "player" : "creature"),GUID_LOPART(guid));
// stop attack state at client
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("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, (4+20)); // 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);
}
@@ -0,0 +1,84 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 Trinity <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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#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 *pl;
Player *plTarget;
if (!GetPlayer()->duel) // ignore accept from duel-sender
return;
recvPacket >> guid;
pl = GetPlayer();
plTarget = pl->duel->opponent;
if (pl == pl->duel->initiator || !plTarget || pl == plTarget || pl->duel->startTime != 0 || plTarget->duel->startTime != 0)
return;
//sLog.outDebug("WORLD: received CMSG_DUEL_ACCEPTED");
DEBUG_LOG("Player 1 is: %u (%s)", pl->GetGUIDLow(),pl->GetName());
DEBUG_LOG("Player 2 is: %u (%s)", plTarget->GetGUIDLow(),plTarget->GetName());
time_t now = time(NULL);
pl->duel->startTimer = now;
plTarget->duel->startTimer = now;
pl->SendDuelCountdown(3000);
plTarget->SendDuelCountdown(3000);
}
void WorldSession::HandleDuelCancelledOpcode(WorldPacket& recvPacket)
{
//sLog.outDebug("WORLD: received CMSG_DUEL_CANCELLED");
// 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;
}
// player either discarded the duel using the "discard button"
// or used "/forfeit" before countdown reached 0
uint64 guid;
recvPacket >> guid;
GetPlayer()->DuelComplete(DUEL_INTERUPTED);
}
@@ -0,0 +1,940 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 Trinity <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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "Common.h"
#include "Database/DatabaseEnv.h"
#include "Opcodes.h"
#include "Log.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "World.h"
#include "ObjectMgr.h"
#include "Player.h"
#include "Group.h"
#include "SocialMgr.h"
#include "Util.h"
#include "SpellAuras.h"
#include "Vehicle.h"
#include "LFG.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)
{
WorldPacket data(SMSG_PARTY_COMMAND_RESULT, (4+member.size()+1+4+4));
data << (uint32)operation;
data << member;
data << (uint32)res;
data << uint32(0); // 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)
{
std::string membername;
recv_data >> membername;
// attempt add selected player
// cheating
if (!normalizePlayerName(membername))
{
SendPartyResult(PARTY_OP_INVITE, membername, ERR_BAD_PLAYER_NAME_S);
return;
}
Player *player = objmgr.GetPlayer(membername.c_str());
// no player
if (!player)
{
SendPartyResult(PARTY_OP_INVITE, membername, ERR_BAD_PLAYER_NAME_S);
return;
}
// restrict invite to GMs
if (!sWorld.getConfig(CONFIG_ALLOW_GM_GROUP) && !GetPlayer()->isGameMaster() && player->isGameMaster())
return;
// can't group with
if (!sWorld.getConfig(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);
return;
}
if (group)
{
// not have permissions for invite
if (group->isRaidGroup() && !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
//for (int i = 0; i < count; ++i)
// data << uint32(0);
data << uint32(0); // unk
player->GetSession()->SendPacket(&data);
SendLfgUpdatePlayer(LFG_UPDATETYPE_REMOVED_FROM_QUEUE);
SendLfgUpdateParty(LFG_UPDATETYPE_REMOVED_FROM_QUEUE);
SendPartyResult(PARTY_OP_INVITE, membername, ERR_PARTY_RESULT_OK);
}
void WorldSession::HandleGroupAcceptOpcode(WorldPacket & /*recv_data*/)
{
Group *group = GetPlayer()->GetGroupInvite();
if (!group) return;
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;
}
// remove in from ivites in any case
group->RemoveInvite(GetPlayer());
/** error handling **/
/********************/
// not have place
if (group->IsFull())
{
SendPartyResult(PARTY_OP_INVITE, "", ERR_GROUP_FULL);
return;
}
Player* leader = objmgr.GetPlayer(group->GetLeaderGUID());
// forming a new group, create it
if (!group->IsCreated())
{
if (leader)
group->RemoveInvite(leader);
group->Create(group->GetLeaderGUID(), group->GetLeaderName());
objmgr.AddGroup(group);
}
// everything's fine, do it, PLAYER'S GROUP IS SET IN ADDMEMBER!!!
if (!group->AddMember(GetPlayer()->GetGUID(), GetPlayer()->GetName()))
return;
SendLfgUpdatePlayer(LFG_UPDATETYPE_REMOVED_FROM_QUEUE);
for (GroupReference *itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
if (Player *plrg = itr->getSource())
{
plrg->GetSession()->SendLfgUpdatePlayer(LFG_UPDATETYPE_CLEAR_LOCK_LIST);
plrg->GetSession()->SendLfgUpdateParty(LFG_UPDATETYPE_CLEAR_LOCK_LIST);
}
group->BroadcastGroupUpdate();
}
void WorldSession::HandleGroupDeclineOpcode(WorldPacket & /*recv_data*/)
{
Group *group = GetPlayer()->GetGroupInvite();
if (!group) return;
// remember leader if online
Player *leader = objmgr.GetPlayer(group->GetLeaderGUID());
// uninvite, group can be deleted
GetPlayer()->UninviteFromGroup();
if (!leader || !leader->GetSession())
return;
// report
WorldPacket data(SMSG_GROUP_DECLINE, 10); // guess size
data << GetPlayer()->GetName();
leader->GetSession()->SendPacket(&data);
}
void WorldSession::HandleGroupUninviteGuidOpcode(WorldPacket & recv_data)
{
uint64 guid;
recv_data >> guid;
recv_data.read_skip<std::string>(); // 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->IsMember(guid))
{
Player::RemoveFromGroup(grp,guid);
return;
}
if (Player* plr = grp->GetInvited(guid))
{
plr->UninviteFromGroup();
return;
}
SendPartyResult(PARTY_OP_UNINVITE, "", ERR_TARGET_NOT_IN_GROUP_S);
}
void WorldSession::HandleGroupUninviteOpcode(WorldPacket & recv_data)
{
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);
return;
}
if (Player* plr = grp->GetInvited(membername))
{
plr->UninviteFromGroup();
return;
}
SendPartyResult(PARTY_OP_UNINVITE, membername, ERR_TARGET_NOT_IN_GROUP_S);
}
void WorldSession::HandleGroupSetLeaderOpcode(WorldPacket & recv_data)
{
Group *group = GetPlayer()->GetGroup();
if (!group)
return;
uint64 guid;
recv_data >> guid;
Player *player = objmgr.GetPlayer(guid);
/** error handling **/
if (!player || !group->IsLeader(GetPlayer()->GetGUID()) || player->GetGroup() != group)
return;
/********************/
// everything's fine, do it
group->ChangeLeader(guid);
}
void WorldSession::HandleGroupDisbandOpcode(WorldPacket & /*recv_data*/)
{
if (!GetPlayer()->GetGroup())
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();
}
void WorldSession::HandleLootMethodOpcode(WorldPacket & recv_data)
{
Group *group = GetPlayer()->GetGroup();
if (!group)
return;
uint32 lootMethod;
uint64 lootMaster;
uint32 lootThreshold;
recv_data >> lootMethod >> lootMaster >> lootThreshold;
/** 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())
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)
{
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)
{
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)
{
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*/)
{
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)
{
// 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;
/** error handling **/
uint64 senderGuid = GetPlayer()->GetGUID();
if (!group->IsLeader(senderGuid) && !group->IsAssistant(senderGuid))
return;
if (!group->HasFreeSlotSubGroup(groupNr))
return;
/********************/
Player *movedPlayer=objmgr.GetPlayer(name.c_str());
if (!movedPlayer)
return;
//Do not allow leader to change group of player in combat
if (movedPlayer->isInCombat())
return;
// everything's fine, do it
group->ChangeMembersGroup(movedPlayer, groupNr);
}
void WorldSession::HandleGroupAssistantLeaderOpcode(WorldPacket & recv_data)
{
Group *group = GetPlayer()->GetGroup();
if (!group)
return;
uint64 guid;
uint8 flag;
recv_data >> guid;
recv_data >> flag;
/** error handling **/
if (!group->IsLeader(GetPlayer()->GetGUID()))
return;
/********************/
// everything's fine, do it
group->SetAssistant(guid, (flag != 0));
}
void WorldSession::HandlePartyAssignmentOpcode(WorldPacket & recv_data)
{
sLog.outDebug("MSG_PARTY_ASSIGNMENT");
Group *group = GetPlayer()->GetGroup();
if (!group)
return;
uint8 flag, apply;
uint64 guid;
recv_data >> flag >> apply;
recv_data >> guid;
/** error handling **/
uint64 senderGuid = GetPlayer()->GetGUID();
if (!group->IsLeader(senderGuid) && group->IsAssistant(senderGuid))
return;
/********************/
// everything's fine, do it
if (flag == 0)
group->SetMainTank(guid, apply);
else if (flag == 1)
group->SetMainAssistant(guid, apply);
}
void WorldSession::HandleRaidReadyCheckOpcode(WorldPacket & recv_data)
{
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)
{
const 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 (player->GetVehicle()){
Vehicle* vv=player->GetVehicle();
*data << (uint32) vv->GetVehicleInfo()->m_seatID[player->m_movementInfo.t_seat];
}
}
if (mask & GROUP_UPDATE_FLAG_PET_AURAS)
{
if (pet)
{
const 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("WORLD: Received CMSG_REQUEST_PARTY_MEMBER_STATS");
uint64 Guid;
recv_data >> Guid;
Player *player = objmgr.GetPlayer(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("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);
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,251 @@
/*
*
* Copyright (C) 2008-2010 Trinity <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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "LFGMgr.h"
#include "WorldSession.h"
#include "WorldPacket.h"
#include "Player.h"
void WorldSession::HandleLfgJoinOpcode(WorldPacket &recv_data)
{
sLog.outDebug("CMSG_LFG_JOIN");
uint8 numDungeons;
uint32 dungeon;
uint32 roles;
std::string comment;
recv_data >> roles;
recv_data.read_skip<uint8>(); // unk - always 0
recv_data.read_skip<uint8>(); // unk - always 0
recv_data >> numDungeons;
if (!numDungeons)
{
sLog.outError("Invalid CMSG_LFG_JOIN packet sent by %s", GetPlayer()->GetName());
recv_data.rpos(recv_data.wpos());
return;
}
GetPlayer()->m_lookingForGroup.roles = uint8(roles);
for (int8 i = 0 ; i < numDungeons; ++i)
{
recv_data >> dungeon;
// remove the type from the dungeon entry
GetPlayer()->m_lookingForGroup.applyDungeons.insert((dungeon & 0x00FFFFFF));
}
recv_data >> numDungeons; // unk - always 3
for (int8 i = 0 ; i < numDungeons; ++i)
recv_data.read_skip<uint8>(); // unk - always 0
recv_data >> comment;
GetPlayer()->m_lookingForGroup.comment = comment;
sLFGMgr.Join(GetPlayer());
}
void WorldSession::HandleLfgLeaveOpcode(WorldPacket & /*recv_data*/)
{
sLog.outDebug("CMSG_LFG_LEAVE");
// Check cheating - only leader can leave the queue
if (Group *grp = GetPlayer()->GetGroup())
{
if (grp->GetLeaderGUID() != GetPlayer()->GetGUID())
return;
else
sLFGMgr.Leave(GetPlayer(), grp);
}
else
sLFGMgr.Leave(GetPlayer());
}
void WorldSession::HandleLfgSetRolesOpcode(WorldPacket &recv_data)
{
sLog.outDebug("CMSG_LFG_SET_ROLES");
uint8 roles;
recv_data >> roles; // Player Group Roles
Group *grp = GetPlayer()->GetGroup();
if (!grp)
return;
GetPlayer()->m_lookingForGroup.roles = roles;
sLFGMgr.UpdateRoleCheck(grp, GetPlayer());
}
void WorldSession::HandleSetLfgCommentOpcode(WorldPacket & recv_data)
{
sLog.outDebug("CMSG_SET_LFG_COMMENT");
std::string comment;
recv_data >> comment;
GetPlayer()->m_lookingForGroup.comment = comment;
}
void WorldSession::HandleLfgPlayerLockInfoRequestOpcode(WorldPacket &/*recv_data*/)
{
sLog.outDebug("CMSG_LFD_PLAYER_LOCK_INFO_REQUEST");
sLFGMgr.SendLfgPlayerInfo(GetPlayer());
}
void WorldSession::HandleLfgPartyLockInfoRequestOpcode(WorldPacket &/*recv_data*/)
{
sLog.outDebug("CMSG_LFD_PARTY_LOCK_INFO_REQUEST");
sLFGMgr.SendLfgPartyInfo(GetPlayer());
}
void WorldSession::SendLfgUpdatePlayer(uint8 updateType)
{
bool queued = false;
bool extrainfo = false;
switch(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_FOUND:
extrainfo = true;
break;
}
sLog.outDebug("SMSG_LFG_UPDATE_PLAYER");
WorldPacket data(SMSG_LFG_UPDATE_PLAYER, 1 + 1 + (extrainfo ? 1 : 0) * (1 + 1 + 1 + 1 + GetPlayer()->m_lookingForGroup.applyDungeons.size() * 4 + GetPlayer()->m_lookingForGroup.comment.length()));
data << uint8(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
uint8 size = GetPlayer()->m_lookingForGroup.applyDungeons.size();
data << uint8(size);
for (LfgDungeonSet::const_iterator it = GetPlayer()->m_lookingForGroup.applyDungeons.begin(); it != GetPlayer()->m_lookingForGroup.applyDungeons.end(); ++it)
data << uint32(*it);
data << GetPlayer()->m_lookingForGroup.comment;
}
SendPacket(&data);
}
void WorldSession::SendLfgUpdateParty(uint8 updateType)
{
bool join = false;
bool extrainfo = false;
bool queued = false;
switch(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_FOUND:
extrainfo = true;
join = true;
break;
}
sLog.outDebug("SMSG_LFG_UPDATE_PARTY");
WorldPacket data(SMSG_LFG_UPDATE_PARTY, 1 + 1 + (extrainfo ? 1 : 0) * (1 + 1 + 1 + 1 + 1 + GetPlayer()->m_lookingForGroup.applyDungeons.size() * 4 + GetPlayer()->m_lookingForGroup.comment.length()));
data << uint8(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
uint8 size = GetPlayer()->m_lookingForGroup.applyDungeons.size();
data << uint8(size);
for (LfgDungeonSet::const_iterator it = GetPlayer()->m_lookingForGroup.applyDungeons.begin(); it != GetPlayer()->m_lookingForGroup.applyDungeons.end(); ++it)
data << uint32(*it);
data << GetPlayer()->m_lookingForGroup.comment;
}
SendPacket(&data);
}
void WorldSession::SendLfgRoleChosen(uint64 guid, uint8 roles)
{
sLog.outDebug("SMSG_LFG_ROLE_CHOSEN");
WorldPacket data(SMSG_LFG_ROLE_CHOSEN);
data << uint64(guid); // Guid
data << uint8(roles > 0); // Ready
data << uint32(roles); // Roles
SendPacket(&data);
}
void WorldSession::SendLfgJoinResult(uint8 checkResult, uint8 checkValue)
{
if (checkResult == LFG_JOIN_PARTY_NOT_MEET_REQS) // Should never happen - its handled in Mgr
return;
sLog.outDebug("SMSG_LFG_JOIN_RESULT");
WorldPacket data(SMSG_LFG_JOIN_RESULT);
data << uint32(checkResult); // Check Result
data << uint32(checkValue); // Check Value
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("SMSG_LFG_QUEUE_STATUS");
WorldPacket data(SMSG_LFG_QUEUE_STATUS);
data << uint32(dungeon); // Dungeon
data << uint32(avgWaitTime); // Average Wait time
data << uint32(waitTime); // Wait Time
data << uint32(waitTimeTanks); // Wait Tanks
data << uint32(waitTimeHealer); // Wait Healers
data << uint32(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::SendLfgUpdateSearch(bool update)
{
sLog.outDebug("SMSG_LFG_UPDATE_SEARCH");
WorldPacket data(SMSG_LFG_UPDATE_SEARCH);
data << uint8(update); // In Lfg Queue?
SendPacket(&data);
}
@@ -0,0 +1,549 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 Trinity <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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#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("WORLD: CMSG_AUTOSTORE_LOOT_ITEM");
Player *player = GetPlayer();
uint64 lguid = player->GetLootGUID();
Loot *loot;
uint8 lootSlot;
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* pCreature = GetPlayer()->GetMap()->GetCreature(lguid);
bool ok_loot = pCreature && pCreature->isAlive() == (player->getClass() == CLASS_ROGUE && pCreature->lootForPickPocketed);
if (!ok_loot || !pCreature->IsWithinDistInMap(_player,INTERACTION_DISTANCE))
{
player->SendLootRelease(lguid);
return;
}
loot = &pCreature->loot;
}
QuestItem *qitem = NULL;
QuestItem *ffaitem = NULL;
QuestItem *conditem = NULL;
LootItem *item = loot->LootItemInSlot(lootSlot,player,&qitem,&ffaitem,&conditem);
if (!item)
{
player->SendEquipError(EQUIP_ERR_ALREADY_LOOTED, NULL, NULL);
return;
}
// questitems use the blocked field for other purposes
if (!qitem && item->is_blocked)
{
player->SendLootRelease(lguid);
return;
}
ItemPosCountVec dest;
uint8 msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, item->itemid, item->count);
if (msg == EQUIP_ERR_OK)
{
Item * newitem = player->StoreNewItem(dest, item->itemid, true, item->randomPropertyId);
if (qitem)
{
qitem->is_looted = true;
//freeforall is 1 if everyone's supposed to get the quest item.
if (item->freeforall || loot->GetPlayerQuestItems().size() == 1)
player->SendNotifyLootItemRemoved(lootSlot);
else
loot->NotifyQuestItemRemoved(qitem->index);
}
else
{
if (ffaitem)
{
//freeforall case, notify only one player of the removal
ffaitem->is_looted=true;
player->SendNotifyLootItemRemoved(lootSlot);
}
else
{
//not freeforall, notify everyone
if (conditem)
conditem->is_looted=true;
loot->NotifyItemRemoved(lootSlot);
}
}
//if only one person is supposed to loot the item, then set it to looted
if (!item->freeforall)
item->is_looted = true;
--loot->unlootedCount;
player->SendNewItem(newitem, uint32(item->count), false, false, true);
player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM, item->itemid, item->count);
player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE, loot->loot_type, item->count);
player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM, item->itemid, item->count);
}
else
player->SendEquipError(msg, NULL, NULL);
}
void WorldSession::HandleLootMoneyOpcode(WorldPacket & /*recv_data*/)
{
sLog.outDebug("WORLD: CMSG_LOOT_MONEY");
Player *player = GetPlayer();
uint64 guid = player->GetLootGUID();
if (!guid)
return;
Loot *pLoot = NULL;
switch(GUID_HIPART(guid))
{
case HIGHGUID_GAMEOBJECT:
{
GameObject *pGameObject = GetPlayer()->GetMap()->GetGameObject(guid);
// not check distance for GO in case owned GO (fishing bobber case, for example)
if (pGameObject && ((pGameObject->GetOwnerGUID() == _player->GetGUID() || pGameObject->IsWithinDistInMap(_player,INTERACTION_DISTANCE))))
pLoot = &pGameObject->loot;
break;
}
case HIGHGUID_CORPSE: // remove insignia ONLY in BG
{
Corpse *bones = ObjectAccessor::GetCorpse(*GetPlayer(), guid);
if (bones && bones->IsWithinDistInMap(_player,INTERACTION_DISTANCE))
pLoot = &bones->loot;
break;
}
case HIGHGUID_ITEM:
{
if (Item *item = GetPlayer()->GetItemByGuid(guid))
pLoot = &item->loot;
break;
}
case HIGHGUID_UNIT:
{
Creature* pCreature = GetPlayer()->GetMap()->GetCreature(guid);
bool ok_loot = pCreature && pCreature->isAlive() == (player->getClass() == CLASS_ROGUE && pCreature->lootForPickPocketed);
if (ok_loot && pCreature->IsWithinDistInMap(_player,INTERACTION_DISTANCE))
pLoot = &pCreature->loot ;
break;
}
default:
return; // unlootable type
}
if (pLoot)
{
if (!IS_ITEM_GUID(guid) && player->GetGroup()) //item 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* playerGroup = itr->getSource();
if (!playerGroup)
continue;
if (player->IsWithinDistInMap(playerGroup,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false))
playersNear.push_back(playerGroup);
}
uint32 money_per_player = uint32((pLoot->gold)/(playersNear.size()));
for (std::vector<Player*>::const_iterator i = playersNear.begin(); i != playersNear.end(); ++i)
{
(*i)->ModifyMoney(money_per_player);
(*i)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY, money_per_player);
//Offset surely incorrect, but works
WorldPacket data(SMSG_LOOT_MONEY_NOTIFY, 4);
data << uint32(money_per_player);
(*i)->GetSession()->SendPacket(&data);
}
}
else
{
player->ModifyMoney(pLoot->gold);
player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY, pLoot->gold);
}
pLoot->gold = 0;
pLoot->NotifyMoneyRemoved();
}
}
void WorldSession::HandleLootOpcode(WorldPacket & recv_data)
{
sLog.outDebug("WORLD: CMSG_LOOT");
uint64 guid;
recv_data >> guid;
// Check possible cheat
if (!_player->isAlive())
return;
GetPlayer()->SendLoot(guid, LOOT_CORPSE);
}
void WorldSession::HandleLootReleaseOpcode(WorldPacket & recv_data)
{
sLog.outDebug("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(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() >= irand(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);
// if the round robin player release, reset it.
if (player->GetGUID() == loot->roundRobinPlayer)
{
if (Group* pGroup = player->GetGroup())
{
if (pGroup->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;
ItemPrototype const* proto = pItem->GetProto();
// destroy only 5 items from stack in case prospecting and milling
if ((proto->BagFamily & (BAG_FAMILY_MASK_MINING_SUPP|BAG_FAMILY_MASK_HERBS)) &&
proto->Class == ITEM_CLASS_TRADE_GOODS)
{
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* pCreature = GetPlayer()->GetMap()->GetCreature(lguid);
bool ok_loot = pCreature && pCreature->isAlive() == (player->getClass() == CLASS_ROGUE && pCreature->lootForPickPocketed);
if (!ok_loot || !pCreature->IsWithinDistInMap(_player,INTERACTION_DISTANCE))
return;
loot = &pCreature->loot;
if (loot->isLooted())
{
// skip pickpocketing loot for speed, skinning timer redunction is no-op in fact
if (!pCreature->isAlive())
pCreature->AllLootRemovedFromCorpse();
pCreature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
pCreature->SetLootRecipient(NULL);
loot->clear();
}
else
{
// if the round robin player release, reset it.
if (player->GetGUID() == loot->roundRobinPlayer)
{
if (Group* pGroup = player->GetGroup())
{
if (pGroup->GetLootMethod() != MASTER_LOOT)
{
loot->roundRobinPlayer = 0;
pGroup->SendLooter(pCreature, NULL);
// force update of dynamic flags, otherwise other group's players still not able to loot.
pCreature->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("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 *pCreature = GetPlayer()->GetMap()->GetCreature(lootguid);
if (!pCreature)
return;
pLoot = &pCreature->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("AutoLootItem: 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;
uint8 msg = target->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, item.itemid, item.count);
if (msg != EQUIP_ERR_OK)
{
target->SendEquipError(msg, NULL, NULL);
_player->SendEquipError(msg, NULL, NULL); // send duplicate of error massage to master looter
return;
}
// not move item from loot to target inventory
Item * newitem = target->StoreNewItem(dest, item.itemid, true, item.randomPropertyId);
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;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,746 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 Trinity <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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "Common.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "Opcodes.h"
#include "Log.h"
#include "Corpse.h"
#include "Player.h"
#include "Vehicle.h"
#include "SpellAuras.h"
#include "MapManager.h"
#include "Transports.h"
#include "BattleGround.h"
#include "WaypointMovementGenerator.h"
#include "InstanceSaveMgr.h"
#include "ObjectMgr.h"
void WorldSession::HandleMoveWorldportAckOpcode(WorldPacket & /*recv_data*/)
{
sLog.outDebug("WORLD: got MSG_MOVE_WORLDPORT_ACK.");
HandleMoveWorldportAckOpcode();
}
void WorldSession::HandleMoveWorldportAckOpcode()
{
// ignore unexpected far teleports
if (!GetPlayer()->IsBeingTeleportedFar())
return;
// 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 = objmgr.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;
GetPlayer()->SetSemaphoreTeleportFar(false);
Map * oldMap = GetPlayer()->GetMap();
assert(oldMap);
if (GetPlayer()->IsInWorld())
{
sLog.outCrash("Player is still in world when teleported from map %u! to new map %u", oldMap->GetId(), loc.GetMapId());
oldMap->Remove(GetPlayer(), false);
}
// relocate the player to the teleport destination
Map * newMap = MapManager::Instance().CreateMap(loc.GetMapId(), GetPlayer(), 0);
// 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()->Add(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 (uint32 timeReset = sInstanceSaveManager.GetResetTimeFor(mEntry->MapID,diff))
{
uint32 timeleft = 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);
// resummon pet
GetPlayer()->ResummonPetTemporaryUnSummonedIfAny();
//lets process all delayed operations on successful teleport
GetPlayer()->ProcessDelayedOperations();
}
void WorldSession::HandleMoveTeleportAck(WorldPacket& recv_data)
{
sLog.outDebug("MSG_MOVE_TELEPORT_ACK");
uint64 guid;
if (!recv_data.readPackGUID(guid))
return;
uint32 flags, time;
recv_data >> flags >> time;
DEBUG_LOG("Guid " UI64FMTD, guid);
DEBUG_LOG("Flags %u, time %u", flags, time/IN_MILISECONDS);
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->SetPosition(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);
}
// 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();
//sLog.outDebug("WORLD: Recvd %s (%u, 0x%X) opcode", LookupOpcodeName(opcode), opcode, opcode);
recv_data.hexlike();
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.rpos(recv_data.wpos()); // prevent warnings spam
return;
}
/* extract packet */
uint64 guid;
if (!recv_data.readPackGUID(guid))
return;
MovementInfo movementInfo;
movementInfo.guid = guid;
ReadMovementInfo(recv_data, &movementInfo);
/*----------------*/
/* if (recv_data.size() != recv_data.rpos())
{
sLog.outError("MovementHandler: player %s (guid %d, account %u) sent a packet (opcode %u) that is " SIZEFMTD " bytes larger than it should be. Kicked as cheater.", _player->GetName(), _player->GetGUIDLow(), _player->GetSession()->GetAccountId(), recv_data.GetOpcode(), recv_data.size() - recv_data.rpos());
KickPlayer();*/
recv_data.rpos(recv_data.wpos()); // prevent warnings spam
/* return;
}*/
if (!Trinity::IsValidMapCoord(movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o))
{
recv_data.rpos(recv_data.wpos()); // 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_x > 50 || movementInfo.t_y > 50 || movementInfo.t_z > 50)
{
recv_data.rpos(recv_data.wpos()); // prevent warnings spam
return;
}
if (!Trinity::IsValidMapCoord(movementInfo.x+movementInfo.t_x, movementInfo.y + movementInfo.t_y,
movementInfo.z + movementInfo.t_z, movementInfo.o + movementInfo.t_o))
{
recv_data.rpos(recv_data.wpos()); // 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 unmount if the guid can be found in the transport list
for (MapManager::TransportSet::const_iterator iter = MapManager::Instance().m_Transports.begin(); iter != MapManager::Instance().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_x = 0.0f;
movementInfo.t_y = 0.0f;
movementInfo.t_z = 0.0f;
movementInfo.t_o = 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.x, movementInfo.y, movementInfo.z));
}
/*----------------------*/
/* 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.o);
return;
}
mover->SetPosition(movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o);
if (plMover) // nothing is charmed, or player charmed
{
plMover->UpdateFallInformationIfNeed(movementInfo, opcode);
if (movementInfo.z < -500.0f)
{
if (plMover->InBattleGround()
&& plMover->GetBattleGround()
&& plMover->GetBattleGround()->HandlePlayerUnderMap(_player))
{
// do nothing, the handle already did if returned true
}
else
{
// 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());
// pl can be alive if GM/etc
if (!plMover->isAlive())
{
// change the death state to CORPSE to prevent the death timer from
// starting in the next player update
plMover->KillPlayer();
plMover->BuildPlayerRepop();
}
}
// cancel the death timer here if started
plMover->RepopAtGraveyard();
}
}
}
/*else // creature charmed
{
if (mover->canFly())
{
bool flying = mover->IsFlying();
if (flying != ((mover->GetByteValue(UNIT_FIELD_BYTES_1, 3) & 0x02) ? true : false))
mover->SetFlying(flying);
}
}*/
//sLog.outString("Receive Movement Packet %s:", opcodeTable[recv_data.GetOpcode()]);
//mover->OutMovementInfo();
}
void WorldSession::HandleForceSpeedChangeAck(WorldPacket &recv_data)
{
uint32 opcode = recv_data.GetOpcode();
sLog.outDebug("WORLD: Recvd %s (%u, 0x%X) opcode", LookupOpcodeName(opcode), opcode, opcode);
/* extract packet */
uint64 guid;
uint32 unk1;
float newspeed;
if (!recv_data.readPackGUID(guid))
return;
// now can skip not our packet
if (_player->GetGUID() != guid)
{
recv_data.rpos(recv_data.wpos()); // 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("WORLD: Recvd CMSG_SET_ACTIVE_MOVER");
uint64 guid;
recv_data >> guid;
if (GetPlayer()->IsInWorld())
if (Unit *mover = ObjectAccessor::GetUnit(*GetPlayer(), guid))
{
GetPlayer()->SetMover(mover);
if (mover != GetPlayer() && mover->canFly())
{
WorldPacket data(SMSG_MOVE_SET_CAN_FLY, 12);
data.append(mover->GetPackGUID());
data << uint32(0);
SendPacket(&data);
}
}
else
{
sLog.outError("HandleSetActiveMoverOpcode: incorrect mover guid: mover is " UI64FMTD " and should be " UI64FMTD, guid, _player->m_mover->GetGUID());
GetPlayer()->SetMover(GetPlayer());
}
}
void WorldSession::HandleMoveNotActiveMover(WorldPacket &recv_data)
{
sLog.outDebug("WORLD: Recvd CMSG_MOVE_NOT_ACTIVE_MOVER");
uint64 old_mover_guid;
if (!recv_data.readPackGUID(old_mover_guid))
return;
/*if (_player->m_mover->GetGUID() == old_mover_guid)
{
sLog.outError("HandleMoveNotActiveMover: incorrect mover guid: mover is " I64FMT " and should be " I64FMT " instead of " I64FMT, _player->m_mover->GetGUID(), _player->GetGUID(), old_mover_guid);
recv_data.rpos(recv_data.wpos()); // prevent warnings spam
return;
}*/
MovementInfo mi;
mi.guid = old_mover_guid;
ReadMovementInfo(recv_data, &mi);
//ReadMovementInfo(recv_data, &_player->m_mover->m_movementInfo);
_player->m_movementInfo = mi;
}
void WorldSession::HandleDismissControlledVehicle(WorldPacket &recv_data)
{
sLog.outDebug("WORLD: Recvd CMSG_DISMISS_CONTROLLED_VEHICLE");
recv_data.hexlike();
uint64 vehicleGUID = _player->GetCharmGUID();
if (!vehicleGUID) // something wrong here...
{
recv_data.rpos(recv_data.wpos()); // prevent warnings spam
return;
}
uint64 guid;
if (!recv_data.readPackGUID(guid))
return;
MovementInfo mi;
mi.guid = guid;
ReadMovementInfo(recv_data, &mi);
_player->m_movementInfo = mi;
/*
ReadMovementInfo(recv_data, &_player->m_mover->m_movementInfo);*/
_player->ExitVehicle();
}
void WorldSession::HandleChangeSeatsOnControlledVehicle(WorldPacket &recv_data)
{
sLog.outDebug("WORLD: Recvd CMSG_CHANGE_SEATS_ON_CONTROLLED_VEHICLE");
recv_data.hexlike();
Unit* vehicle_base = GetPlayer()->GetVehicleBase();
if (!vehicle_base)
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
if (!recv_data.readPackGUID(guid))
return;
ReadMovementInfo(recv_data, &vehicle_base->m_movementInfo);
uint64 accessory; // accessory guid
if (!recv_data.readPackGUID(accessory))
return;
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))
GetPlayer()->EnterVehicle(vehicle, seatId);
}
}
break;
case CMSG_REQUEST_VEHICLE_SWITCH_SEAT:
{
uint64 guid; // current vehicle guid
if (!recv_data.readPackGUID(guid))
return;
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))
GetPlayer()->EnterVehicle(vehicle, seatId);
}
break;
default:
break;
}
}
void WorldSession::HandleEnterPlayerVehicle(WorldPacket &data)
{
// Read guid
uint64 guid;
data >> guid;
if (Player* pl=ObjectAccessor::FindPlayer(guid))
{
if (!pl->GetVehicleKit())
return;
if (!pl->IsInRaidWith(_player))
return;
if (!pl->IsWithinDistInMap(_player,INTERACTION_DISTANCE))
return;
_player->EnterVehicle(pl);
}
}
void WorldSession::HandleEjectPasenger(WorldPacket &data)
{
if (data.GetOpcode() == CMSG_EJECT_PASSENGER)
{
if (Vehicle* Vv= _player->GetVehicleKit())
{
uint64 guid;
data >> guid;
if (Player* Pl=ObjectAccessor::FindPlayer(guid))
Pl->ExitVehicle();
}
}
}
void WorldSession::HandleRequestVehicleExit(WorldPacket &recv_data)
{
sLog.outDebug("WORLD: Recvd CMSG_REQUEST_VEHICLE_EXIT");
recv_data.hexlike();
GetPlayer()->ExitVehicle();
}
void WorldSession::HandleMountSpecialAnimOpcode(WorldPacket& /*recvdata*/)
{
//sLog.outDebug("WORLD: Recvd CMSG_MOUNTSPECIAL_ANIM");
WorldPacket data(SMSG_MOUNTSPECIAL_ANIM, 8);
data << uint64(GetPlayer()->GetGUID());
GetPlayer()->SendMessageToSet(&data, false);
}
void WorldSession::HandleMoveKnockBackAck(WorldPacket & recv_data)
{
sLog.outDebug("CMSG_MOVE_KNOCK_BACK_ACK");
uint64 guid; // guid - unused
if (!recv_data.readPackGUID(guid))
return;
recv_data.read_skip<uint32>(); // unk
MovementInfo movementInfo;
ReadMovementInfo(recv_data, &movementInfo);
}
void WorldSession::HandleMoveHoverAck(WorldPacket& recv_data)
{
sLog.outDebug("CMSG_MOVE_HOVER_ACK");
uint64 guid; // guid - unused
if (!recv_data.readPackGUID(guid))
return;
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("CMSG_MOVE_WATER_WALK_ACK");
uint64 guid; // guid - unused
if (!recv_data.readPackGUID(guid))
return;
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);
}
@@ -0,0 +1,869 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 Trinity <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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "Common.h"
#include "Language.h"
#include "Database/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 "Guild.h"
#include "ScriptMgr.h"
void WorldSession::HandleTabardVendorActivateOpcode(WorldPacket & recv_data)
{
uint64 guid;
recv_data >> guid;
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(guid,UNIT_NPC_FLAG_TABARDDESIGNER);
if (!unit)
{
sLog.outDebug("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("WORLD: Received CMSG_BANKER_ACTIVATE");
recv_data >> guid;
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(guid,UNIT_NPC_FLAG_BANKER);
if (!unit)
{
sLog.outDebug("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("WORLD: SendTrainerList");
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(guid,UNIT_NPC_FLAG_TRAINER);
if (!unit)
{
sLog.outDebug("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;
CreatureInfo const *ci = unit->GetCreatureInfo();
if (!ci)
{
sLog.outDebug("WORLD: SendTrainerList - (GUID: %u) NO CREATUREINFO!",GUID_LOPART(guid));
return;
}
TrainerSpellData const* trainer_spells = unit->GetTrainerSpells();
if (!trainer_spells)
{
sLog.outDebug("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;
}
if (spellmgr.IsPrimaryProfessionFirstRankSpell(tSpell->learnedSpell[i]))
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 (SpellChainNode const* chain_node = spellmgr.GetSpellChainNode(tSpell->learnedSpell[i]))
{
if (chain_node->prev)
{
data << uint32(chain_node->prev);
++maxReq;
}
}
if (maxReq == 3)
break;
SpellsRequiringSpellMapBounds spellsRequired = spellmgr.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("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("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->GetMoney() < nSpellCost)
return;
_player->ModifyMoney(-int32(nSpellCost));
WorldPacket data(SMSG_PLAY_SPELL_VISUAL, 12); // visual effect on trainer
data << uint64(guid);
data << uint32(0xB3); // index from SpellVisualKit.dbc
SendPacket(&data);
data.Initialize(SMSG_PLAY_SPELL_IMPACT, 12); // visual effect on player
data << uint64(_player->GetGUID());
data << uint32(0x016A); // index from SpellVisualKit.dbc
SendPacket(&data);
// learn explicitly or cast explicitly
if (trainer_spell->IsCastable())
_player->CastSpell(_player,trainer_spell->spell,true);
else
_player->learnSpell(spellId, false);
data.Initialize(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("WORLD: Received CMSG_GOSSIP_HELLO");
uint64 guid;
recv_data >> guid;
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE);
if (!unit)
{
sLog.outDebug("WORLD: HandleGossipHelloOpcode - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(guid)));
return;
}
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->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.GossipHello(_player, unit))
{
// _player->TalkedToCreature(unit->GetEntry(), unit->GetGUID());
_player->PrepareGossipMenu(unit, unit->GetCreatureInfo()->GossipMenuId, true);
_player->SendPreparedGossip(unit);
}
}
/*void WorldSession::HandleGossipSelectOptionOpcode(WorldPacket & recv_data)
{
sLog.outDebug("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("reading string");
recv_data >> code;
sLog.outDebug("string read: %s", code.c_str());
}
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE);
if (!unit)
{
sLog.outDebug("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->GossipSelect (_player, unit, _player->PlayerTalkClass->GossipOptionSender (option), _player->PlayerTalkClass->GossipOptionAction (option)))
unit->OnGossipSelect (_player, option);
}
}*/
void WorldSession::HandleSpiritHealerActivateOpcode(WorldPacket & recv_data)
{
sLog.outDebug("WORLD: CMSG_SPIRIT_HEALER_ACTIVATE");
uint64 guid;
recv_data >> guid;
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_SPIRITHEALER);
if (!unit)
{
sLog.outDebug("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 = objmgr.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 = objmgr.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("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
CharacterDatabase.PExecute("UPDATE character_homebind SET map = '%u', zone = '%u', position_x = '%f', position_y = '%f', position_z = '%f' WHERE guid = '%u'",
_player->GetMapId(), _player->GetAreaId(), _player->GetPositionX(), _player->GetPositionY(), _player->GetPositionZ(), _player->GetGUIDLow());
_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->CloseGossip();
}
void WorldSession::HandleListStabledPetsOpcode(WorldPacket & recv_data)
{
sLog.outDebug("WORLD: Recv MSG_LIST_STABLED_PETS");
uint64 npcGUID;
recv_data >> npcGUID;
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(npcGUID, UNIT_NPC_FLAG_STABLEMASTER);
if (!unit)
{
sLog.outDebug("WORLD: HandleListStabledPetsOpcode - 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);
// 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)
{
sLog.outDebug("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;
}
// 0 1 2 3 4
QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT owner, id, entry, level, name FROM character_pet WHERE owner = '%u' AND slot >= '%u' AND slot <= '%u' ORDER BY slot",
_player->GetGUIDLow(),PET_SAVE_FIRST_STABLE_SLOT,PET_SAVE_LAST_STABLE_SLOT);
if (result)
{
do
{
Field *fields = result->Fetch();
data << uint32(fields[1].GetUInt32()); // petnumber
data << uint32(fields[2].GetUInt32()); // creature entry
data << uint32(fields[3].GetUInt32()); // 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::HandleStablePet(WorldPacket & recv_data)
{
sLog.outDebug("WORLD: Recv CMSG_STABLE_PET");
uint64 npcGUID;
recv_data >> npcGUID;
if (!GetPlayer()->isAlive())
return;
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(npcGUID, UNIT_NPC_FLAG_STABLEMASTER);
if (!unit)
{
sLog.outDebug("WORLD: HandleStablePet - 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);
Pet *pet = _player->GetPet();
// can't place in stable dead pet
if (!pet||!pet->isAlive()||pet->getPetType() != HUNTER_PET)
{
WorldPacket data(SMSG_STABLE_RESULT, 1);
data << uint8(0x06);
SendPacket(&data);
return;
}
uint32 free_slot = 1;
QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT owner,slot,id FROM character_pet WHERE owner = '%u' AND slot >= '%u' AND slot <= '%u' ORDER BY slot ",
_player->GetGUIDLow(),PET_SAVE_FIRST_STABLE_SLOT,PET_SAVE_LAST_STABLE_SLOT);
if (result)
{
do
{
Field *fields = result->Fetch();
uint32 slot = fields[1].GetUInt32();
// slots ordered in query, and if not equal then free
if (slot != free_slot)
break;
// this slot not free, skip
++free_slot;
}while (result->NextRow());
}
WorldPacket data(SMSG_STABLE_RESULT, 1);
if (free_slot > 0 && free_slot <= GetPlayer()->m_stableSlots)
{
_player->RemovePet(pet,PetSaveMode(free_slot));
data << uint8(0x08);
}
else
data << uint8(0x06);
SendPacket(&data);
}
void WorldSession::HandleUnstablePet(WorldPacket & recv_data)
{
sLog.outDebug("WORLD: Recv CMSG_UNSTABLE_PET.");
uint64 npcGUID;
uint32 petnumber;
recv_data >> npcGUID >> petnumber;
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(npcGUID, UNIT_NPC_FLAG_STABLEMASTER);
if (!unit)
{
sLog.outDebug("WORLD: HandleUnstablePet - 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);
uint32 creature_id = 0;
{
QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT entry FROM character_pet WHERE owner = '%u' AND id = '%u' AND slot >='%u' AND slot <= '%u'",
_player->GetGUIDLow(),petnumber,PET_SAVE_FIRST_STABLE_SLOT,PET_SAVE_LAST_STABLE_SLOT);
if (result)
{
Field *fields = result->Fetch();
creature_id = fields[0].GetUInt32();
}
}
if (!creature_id)
{
WorldPacket data(SMSG_STABLE_RESULT, 1);
data << uint8(0x06);
SendPacket(&data);
return;
}
CreatureInfo const* creatureInfo = objmgr.GetCreatureTemplate(creature_id);
if (!creatureInfo || !creatureInfo->isTameable(_player->CanTameExoticPets()))
{
WorldPacket data(SMSG_STABLE_RESULT, 1);
data << uint8(0x06);
SendPacket(&data);
return;
}
Pet* pet = _player->GetPet();
if (pet && pet->isAlive())
{
WorldPacket data(SMSG_STABLE_RESULT, 1);
data << uint8(0x06);
SendPacket(&data);
return;
}
// delete dead pet
if (pet)
_player->RemovePet(pet,PET_SAVE_AS_DELETED);
Pet *newpet = new Pet(_player, HUNTER_PET);
if (!newpet->LoadPetFromDB(_player,creature_id,petnumber))
{
delete newpet;
newpet = NULL;
WorldPacket data(SMSG_STABLE_RESULT, 1);
data << uint8(0x06);
SendPacket(&data);
return;
}
WorldPacket data(SMSG_STABLE_RESULT, 1);
data << uint8(0x09);
SendPacket(&data);
}
void WorldSession::HandleBuyStableSlot(WorldPacket & recv_data)
{
sLog.outDebug("WORLD: Recv CMSG_BUY_STABLE_SLOT.");
uint64 npcGUID;
recv_data >> npcGUID;
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(npcGUID, UNIT_NPC_FLAG_STABLEMASTER);
if (!unit)
{
sLog.outDebug("WORLD: HandleBuyStableSlot - 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);
WorldPacket data(SMSG_STABLE_RESULT, 200);
if (GetPlayer()->m_stableSlots < MAX_PET_STABLES)
{
StableSlotPricesEntry const *SlotPrice = sStableSlotPricesStore.LookupEntry(GetPlayer()->m_stableSlots+1);
if (_player->GetMoney() >= SlotPrice->Price)
{
++GetPlayer()->m_stableSlots;
_player->ModifyMoney(-int32(SlotPrice->Price));
data << uint8(0x0A); // success buy
}
else
data << uint8(0x06);
}
else
data << uint8(0x06);
SendPacket(&data);
}
void WorldSession::HandleStableRevivePet(WorldPacket &/* recv_data */)
{
sLog.outDebug("HandleStableRevivePet: Not implemented");
}
void WorldSession::HandleStableSwapPet(WorldPacket & recv_data)
{
sLog.outDebug("WORLD: Recv CMSG_STABLE_SWAP_PET.");
uint64 npcGUID;
uint32 pet_number;
recv_data >> npcGUID >> pet_number;
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(npcGUID, UNIT_NPC_FLAG_STABLEMASTER);
if (!unit)
{
sLog.outDebug("WORLD: HandleStableSwapPet - 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);
WorldPacket data(SMSG_STABLE_RESULT, 200); // guess size
Pet* pet = _player->GetPet();
if (!pet || pet->getPetType() != HUNTER_PET)
return;
// find swapped pet slot in stable
QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT slot,entry FROM character_pet WHERE owner = '%u' AND id = '%u'",
_player->GetGUIDLow(),pet_number);
if (!result)
return;
Field *fields = result->Fetch();
uint32 slot = fields[0].GetUInt32();
uint32 creature_id = fields[1].GetUInt32();
if (!creature_id)
{
WorldPacket data(SMSG_STABLE_RESULT, 1);
data << uint8(0x06);
SendPacket(&data);
return;
}
CreatureInfo const* creatureInfo = objmgr.GetCreatureTemplate(creature_id);
if (!creatureInfo || !creatureInfo->isTameable(_player->CanTameExoticPets()))
{
WorldPacket data(SMSG_STABLE_RESULT, 1);
data << uint8(0x06);
SendPacket(&data);
return;
}
// move alive pet to slot or delete dead pet
_player->RemovePet(pet,pet->isAlive() ? PetSaveMode(slot) : PET_SAVE_AS_DELETED);
// summon unstabled pet
Pet *newpet = new Pet(_player);
if (!newpet->LoadPetFromDB(_player,creature_id,pet_number))
{
delete newpet;
data << uint8(0x06);
}
else
data << uint8(0x09);
SendPacket(&data);
}
void WorldSession::HandleRepairItemOpcode(WorldPacket & recv_data)
{
sLog.outDebug("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("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);
uint32 TotalCost = 0;
if (itemGUID)
{
sLog.outDebug("ITEM: Repair item, itemGUID = %u, npcGUID = %u", GUID_LOPART(itemGUID), GUID_LOPART(npcGUID));
Item* item = _player->GetItemByGuid(itemGUID);
if (item)
TotalCost= _player->DurabilityRepair(item->GetPos(),true,discountMod,guildBank>0?true:false);
}
else
{
sLog.outDebug("ITEM: Repair all items, npcGUID = %u", GUID_LOPART(npcGUID));
TotalCost = _player->DurabilityRepairAll(true,discountMod,guildBank>0?true:false);
}
if (guildBank)
{
uint32 GuildId = _player->GetGuildId();
if (!GuildId)
return;
Guild *pGuild = objmgr.GetGuildById(GuildId);
if (!pGuild)
return;
pGuild->LogBankEvent(GUILD_BANK_LOG_REPAIR_MONEY, 0, _player->GetGUIDLow(), TotalCost);
pGuild->SendMoneyInfo(this, _player->GetGUIDLow());
}
}
@@ -0,0 +1,79 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 Trinity <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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __NPCHANDLER_H
#define __NPCHANDLER_H
// GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push,N), also any gcc version not support it at some platform
#if defined(__GNUC__)
#pragma pack(1)
#else
#pragma pack(push,1)
#endif
struct PageText
{
uint32 Page_ID;
char * Text;
uint32 Next_Page;
};
// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some platform
#if defined(__GNUC__)
#pragma pack()
#else
#pragma pack(pop)
#endif
struct QEmote
{
uint32 _Emote;
uint32 _Delay;
};
struct GossipTextOption
{
std::string Text_0;
std::string Text_1;
uint32 Language;
float Probability;
QEmote Emotes[3];
};
struct GossipText
{
GossipTextOption Options[8];
};
struct PageTextLocale
{
std::vector<std::string> Text;
};
struct NpcTextLocale
{
NpcTextLocale() { Text_0.resize(8); Text_1.resize(8); }
std::vector<std::vector<std::string> > Text_0;
std::vector<std::vector<std::string> > Text_1;
};
#endif
@@ -0,0 +1,813 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 Trinity <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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#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"
void WorldSession::HandleDismissCritter(WorldPacket &recv_data)
{
uint64 guid;
recv_data >> guid;
sLog.outDebug("WORLD: Received CMSG_DISMISS_CRITTER for GUID %u", guid);
Unit* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid);
if (!pet)
{
sLog.outError("Vanitypet %u does not exist", uint32(GUID_LOPART(guid)));
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 is %u, spellid is %u, target %u.", uint32(GUID_LOPART(guid1)), uint32(flag), spellid, uint32(GUID_LOPART(guid2)));
if (!pet)
{
sLog.outError("Pet %u not exist.", uint32(GUID_LOPART(guid1)));
return;
}
if (pet != GetPlayer()->GetFirstControlled())
{
sLog.outError("HandlePetAction.Pet %u isn't pet of player %s.", uint32(GUID_LOPART(guid1)), GetPlayer()->GetName());
return;
}
if (!pet->isAlive())
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::HandlePetActionHelper(Unit *pet, uint64 guid1, uint16 spellid, uint16 flag, uint64 guid2)
{
CharmInfo *charmInfo = pet->GetCharmInfo();
if (!charmInfo)
{
sLog.outError("WorldSession::HandlePetAction: object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", 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()->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 (!pet->canAttack(TargetUnit))
return;
// Not let attack through obstructions
if (sWorld.getConfig(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
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid);
if (!spellInfo)
{
sLog.outError("WORLD: unknown PET spell id %i", spellid);
return;
}
if (spellInfo->StartRecoveryCategory > 0)
if (pet->ToCreature()->GetGlobalCooldown() > 0)
return;
for (uint32 i = 0; i < 3; ++i)
{
if (spellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_AREA_ENEMY_SRC || spellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_AREA_ENEMY_DST || spellInfo->EffectImplicitTargetA[i] == TARGET_DEST_DYNOBJ_ENEMY)
return;
}
// do not cast not learned spells
if (!pet->HasSpell(spellid) || IsPassiveSpell(spellid))
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, false);
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+4+7+1));
data << uint32(petnumber);
data << "Unknown";
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);
}
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 or pet owner.");
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)))
{
//sign for autocast
if (act_state == ACT_ENABLED && spell_id)
{
if (pet->GetTypeId() == TYPEID_UNIT && pet->ToCreature()->isPet())
((Pet*)pet)->ToggleAutocast(spell_id, true);
else
charmInfo->ToggleCreatureAutocast(spell_id, true);
}
//sign for no/turn off autocast
else if (act_state == ACT_DISABLED && spell_id)
{
if (pet->GetTypeId() == TYPEID_UNIT && pet->ToCreature()->isPet())
((Pet*)pet)->ToggleAutocast(spell_id, false);
else
charmInfo->ToggleCreatureAutocast(spell_id, 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::GetPet(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 (objmgr.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(GetMainPartOfName(wname,0),declinedname))
{
SendPetNameInvalid(PET_NAME_DECLENSION_DOESNT_MATCH_BASE_NAME, name, &declinedname);
return;
}
}
CharacterDatabase.BeginTransaction();
if (isdeclined)
{
for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
CharacterDatabase.escape_string(declinedname.name[i]);
CharacterDatabase.PExecute("DELETE FROM character_pet_declinedname WHERE owner = '%u' AND id = '%u'", _player->GetGUIDLow(), pet->GetCharmInfo()->GetPetNumber());
CharacterDatabase.PExecute("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.escape_string(name);
CharacterDatabase.PExecute("UPDATE character_pet SET name = '%s', renamed = '1' WHERE owner = '%u' AND id = '%u'", name.c_str(), _player->GetGUIDLow(), pet->GetCharmInfo()->GetPetNumber());
CharacterDatabase.CommitTransaction();
pet->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, time(NULL));
}
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) > 0 ?(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;
}
// do not add not learned spells/ passive spells
if (!pet->HasSpell(spellid) || IsAutocastableSpell(spellid))
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(spellid, state);
else
pet->GetCharmInfo()->ToggleCreatureAutocast(spellid, state);
charmInfo->SetSpellAutocast(spellid,state);
}
void WorldSession::HandlePetCastSpellOpcode(WorldPacket& recvPacket)
{
sLog.outDetail("WORLD: CMSG_PET_CAST_SPELL");
uint64 guid;
uint32 spellid;
uint8 cast_count;
uint8 unk_flags; // flags (if 0x02 - some additional data are received)
recvPacket >> guid >> cast_count >> spellid >> unk_flags;
sLog.outDebug("WORLD: CMSG_PET_CAST_SPELL, cast_count: %u, spellid %u, unk_flags %u", cast_count, spellid, unk_flags);
// 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;
}
SpellEntry const *spellInfo = sSpellStore.LookupEntry(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->ToCreature()->GetGlobalCooldown() > 0)
{
caster->SendPetCastFail(spellid, SPELL_FAILED_NOT_READY);
return;
}
// do not cast not learned spells
if (!caster->HasSpell(spellid) || IsPassiveSpell(spellid))
return;
SpellCastTargets targets;
if (!targets.read(&recvPacket,caster))
return;
caster->clearUnitState(UNIT_STAT_FOLLOW);
Spell *spell = new Spell(caster, spellInfo, spellid == 33395); // water elemental can cast freeze as triggered
spell->m_cast_count = spellid == 33395 ? 0 : cast_count; // 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("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("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);
}
@@ -0,0 +1,938 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 Trinity <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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "Common.h"
#include "Language.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "World.h"
#include "ObjectMgr.h"
#include "Log.h"
#include "Opcodes.h"
#include "Guild.h"
#include "ArenaTeam.h"
#include "GossipDef.h"
#include "SocialMgr.h"
/*enum PetitionType // dbc data
{
PETITION_TYPE_GUILD = 1,
PETITION_TYPE_ARENA_TEAM = 3
};*/
// Charters ID in item_template
#define GUILD_CHARTER 5863
#define GUILD_CHARTER_COST 1000 // 10 S
#define ARENA_TEAM_CHARTER_2v2 23560
#define ARENA_TEAM_CHARTER_2v2_COST 800000 // 80 G
#define ARENA_TEAM_CHARTER_3v3 23561
#define ARENA_TEAM_CHARTER_3v3_COST 1200000 // 120 G
#define ARENA_TEAM_CHARTER_5v5 23562
#define ARENA_TEAM_CHARTER_5v5_COST 2000000 // 200 G
void WorldSession::HandlePetitionBuyOpcode(WorldPacket & recv_data)
{
sLog.outDebug("Received opcode CMSG_PETITION_BUY");
recv_data.hexlike();
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("Petitioner with GUID %u tried sell petition: name %s", GUID_LOPART(guidNPC), name.c_str());
// prevent cheating
Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(guidNPC,UNIT_NPC_FLAG_PETITIONER);
if (!pCreature)
{
sLog.outDebug("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 (pCreature->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 = 9;
}
else
{
// TODO: find correct opcode
if (_player->getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
{
SendNotification(LANG_ARENA_ONE_TOOLOW, sWorld.getConfig(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 = 2; // 2v2
break;
case 2:
charterid = ARENA_TEAM_CHARTER_3v3;
cost = ARENA_TEAM_CHARTER_3v3_COST;
type = 3; // 3v3
break;
case 3:
charterid = ARENA_TEAM_CHARTER_5v5;
cost = ARENA_TEAM_CHARTER_5v5_COST;
type = 5; // 5v5
break;
default:
sLog.outDebug("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 == 9)
{
if (objmgr.GetGuildByName(name))
{
SendGuildCommandResult(GUILD_CREATE_S, name, ERR_GUILD_NAME_EXISTS_S);
return;
}
if (objmgr.IsReservedName(name) || !ObjectMgr::IsValidCharterName(name))
{
SendGuildCommandResult(GUILD_CREATE_S, name, ERR_GUILD_NAME_INVALID);
return;
}
}
else
{
if (objmgr.GetArenaTeamByName(name))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ARENA_TEAM_NAME_EXISTS_S);
return;
}
if (objmgr.IsReservedName(name) || !ObjectMgr::IsValidCharterName(name))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ARENA_TEAM_NAME_INVALID);
return;
}
}
ItemPrototype const *pProto = objmgr.GetItemPrototype(charterid);
if (!pProto)
{
_player->SendBuyError(BUY_ERR_CANT_FIND_ITEM, NULL, charterid, 0);
return;
}
if (_player->GetMoney() < cost)
{ //player hasn't got enough money
_player->SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, charterid, 0);
return;
}
ItemPosCountVec dest;
uint8 msg = _player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, charterid, pProto->BuyCount);
if (msg != EQUIP_ERR_OK)
{
_player->SendBuyError(msg, pCreature, charterid, 0);
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_AutoPtr 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("Invalid petition GUIDs: %s", ssInvalidPetitionGUIDs.str().c_str());
CharacterDatabase.escape_string(name);
CharacterDatabase.BeginTransaction();
CharacterDatabase.PExecute("DELETE FROM petition WHERE petitionguid IN (%s)", ssInvalidPetitionGUIDs.str().c_str());
CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE petitionguid IN (%s)", ssInvalidPetitionGUIDs.str().c_str());
CharacterDatabase.PExecute("INSERT INTO petition (ownerguid, petitionguid, name, type) VALUES ('%u', '%u', '%s', '%u')",
_player->GetGUIDLow(), charter->GetGUIDLow(), name.c_str(), type);
CharacterDatabase.CommitTransaction();
}
void WorldSession::HandlePetitionShowSignOpcode(WorldPacket & recv_data)
{
// ok
sLog.outDebug("Received opcode CMSG_PETITION_SHOW_SIGNATURES");
//recv_data.hexlike();
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_AutoPtr 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].GetUInt32();
// if guild petition and has guild => error, return;
if (type == 9 && _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 = result->GetRowCount();
sLog.outDebug("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 (in mangos always same as GUID_LOPART(petitionguid)
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("Received opcode CMSG_PETITION_QUERY"); // ok
//recv_data.hexlike();
uint32 guildguid;
uint64 petitionguid;
recv_data >> guildguid; // in Trinity always same as GUID_LOPART(petitionguid)
recv_data >> petitionguid; // petition guid
sLog.outDebug("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";
uint8 signs = 0;
QueryResult_AutoPtr result = CharacterDatabase.PQuery(
"SELECT ownerguid, name, "
" (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)
{
Field* fields = result->Fetch();
ownerguid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
name = fields[1].GetCppString();
signs = fields[2].GetUInt8();
type = fields[3].GetUInt32();
}
else
{
sLog.outDebug("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 == 9)
{
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 == 9)
data << uint32(0); // 15 0 - guild, 1 - arena team
else
data << uint32(1);
SendPacket(&data);
}
void WorldSession::HandlePetitionRenameOpcode(WorldPacket & recv_data)
{
sLog.outDebug("Received opcode MSG_PETITION_RENAME"); // ok
//recv_data.hexlike();
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_AutoPtr result = CharacterDatabase.PQuery("SELECT type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
if (result)
{
Field* fields = result->Fetch();
type = fields[0].GetUInt32();
}
else
{
sLog.outDebug("CMSG_PETITION_QUERY failed for petition (GUID: %u)", GUID_LOPART(petitionguid));
return;
}
if (type == 9)
{
if (objmgr.GetGuildByName(newname))
{
SendGuildCommandResult(GUILD_CREATE_S, newname, ERR_GUILD_NAME_EXISTS_S);
return;
}
if (objmgr.IsReservedName(newname) || !ObjectMgr::IsValidCharterName(newname))
{
SendGuildCommandResult(GUILD_CREATE_S, newname, ERR_GUILD_NAME_INVALID);
return;
}
}
else
{
if (objmgr.GetArenaTeamByName(newname))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, newname, "", ERR_ARENA_TEAM_NAME_EXISTS_S);
return;
}
if (objmgr.IsReservedName(newname) || !ObjectMgr::IsValidCharterName(newname))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, newname, "", ERR_ARENA_TEAM_NAME_INVALID);
return;
}
}
std::string db_newname = newname;
CharacterDatabase.escape_string(db_newname);
CharacterDatabase.PExecute("UPDATE petition SET name = '%s' WHERE petitionguid = '%u'",
db_newname.c_str(), GUID_LOPART(petitionguid));
sLog.outDebug("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("Received opcode CMSG_PETITION_SIGN"); // ok
//recv_data.hexlike();
Field *fields;
uint64 petitionguid;
uint8 unk;
recv_data >> petitionguid; // petition guid
recv_data >> unk;
QueryResult_AutoPtr 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 plguidlo = _player->GetGUIDLow();
if (GUID_LOPART(ownerguid) == plguidlo)
return;
// not let enemies sign guild charter
if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && GetPlayer()->GetTeam() != objmgr.GetPlayerTeamByGUID(ownerguid))
{
if (type != 9)
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED);
else
SendGuildCommandResult(GUILD_CREATE_S, "", ERR_GUILD_NOT_ALLIED);
return;
}
if (type != 9)
{
if (_player->getLevel() < sWorld.getConfig(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())
{
SendGuildCommandResult(GUILD_INVITE_S, _player->GetName(), ERR_ALREADY_IN_GUILD_S);
return;
}
if (_player->GetGuildIdInvited())
{
SendGuildCommandResult(GUILD_INVITE_S, _player->GetName(), ERR_ALREADY_INVITED_TO_GUILD_S);
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 = objmgr.GetPlayer(ownerguid))
owner->GetSession()->SendPacket(&data);
return;
}
CharacterDatabase.PExecute("INSERT INTO petition_sign (ownerguid,petitionguid, playerguid, player_account) VALUES ('%u', '%u', '%u','%u')", GUID_LOPART(ownerguid),GUID_LOPART(petitionguid), plguidlo,GetAccountId());
sLog.outDebug("PETITION SIGN: GUID %u by player: %s (GUID: %u Account: %u)", GUID_LOPART(petitionguid), _player->GetName(),plguidlo,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 = objmgr.GetPlayer(ownerguid))
owner->GetSession()->SendPacket(&data);
}
void WorldSession::HandlePetitionDeclineOpcode(WorldPacket & recv_data)
{
sLog.outDebug("Received opcode MSG_PETITION_DECLINE"); // ok
//recv_data.hexlike();
uint64 petitionguid;
uint64 ownerguid;
recv_data >> petitionguid; // petition guid
sLog.outDebug("Petition %u declined by %u", GUID_LOPART(petitionguid), _player->GetGUIDLow());
QueryResult_AutoPtr 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 = objmgr.GetPlayer(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("Received opcode CMSG_OFFER_PETITION"); // ok
//recv_data.hexlike();
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_AutoPtr result = CharacterDatabase.PQuery("SELECT type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
if (!result)
return;
Field *fields = result->Fetch();
type = fields[0].GetUInt32();
sLog.outDebug("OFFER PETITION: type %u, GUID1 %u, to player id: %u", type, GUID_LOPART(petitionguid), GUID_LOPART(plguid));
if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && GetPlayer()->GetTeam() != player->GetTeam())
{
if (type != 9)
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED);
else
SendGuildCommandResult(GUILD_CREATE_S, "", ERR_GUILD_NOT_ALLIED);
return;
}
if (type != 9)
{
if (player->getLevel() < sWorld.getConfig(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())
{
SendGuildCommandResult(GUILD_INVITE_S, _player->GetName(), ERR_ALREADY_IN_GUILD_S);
return;
}
if (player->GetGuildIdInvited())
{
SendGuildCommandResult(GUILD_INVITE_S, _player->GetName(), ERR_ALREADY_INVITED_TO_GUILD_S);
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 = 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 (in mangos always same as GUID_LOPART(petition 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("Received opcode CMSG_TURN_IN_PETITION"); // ok
//recv_data.hexlike();
WorldPacket data;
uint64 petitionguid;
uint32 ownerguidlo;
uint32 type;
std::string name;
recv_data >> petitionguid;
sLog.outDebug("Petition %u turned in by %u", GUID_LOPART(petitionguid), _player->GetGUIDLow());
// data
QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT ownerguid, name, type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
if (result)
{
Field *fields = result->Fetch();
ownerguidlo = fields[0].GetUInt32();
name = fields[1].GetCppString();
type = fields[2].GetUInt32();
}
else
{
sLog.outError("petition table has broken data!");
return;
}
if (type == 9)
{
if (_player->GetGuildId())
{
data.Initialize(SMSG_TURN_IN_PETITION_RESULTS, 4);
data << (uint32)PETITION_TURN_ALREADY_IN_GUILD; // already in guild
_player->GetSession()->SendPacket(&data);
return;
}
}
else
{
uint8 slot = ArenaTeam::GetSlotByType(type);
if (slot >= MAX_ARENA_SLOT)
return;
if (_player->GetArenaTeamId(slot))
{
//data.Initialize(SMSG_TURN_IN_PETITION_RESULTS, 4);
//data << (uint32)PETITION_TURN_ALREADY_IN_GUILD; // already in guild
//_player->GetSession()->SendPacket(&data);
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ALREADY_IN_ARENA_TEAM);
return;
}
}
if (_player->GetGUIDLow() != ownerguidlo)
return;
// signs
uint8 signs;
result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
if (result)
signs = result->GetRowCount();
else
signs = 0;
uint32 count;
//if (signs < sWorld.getConfig(CONFIG_MIN_PETITION_SIGNS))
if (type == 9)
count = sWorld.getConfig(CONFIG_MIN_PETITION_SIGNS);
else
count = type-1;
if (signs < count)
{
data.Initialize(SMSG_TURN_IN_PETITION_RESULTS, 4);
data << (uint32)PETITION_TURN_NEED_MORE_SIGNATURES; // need more signatures...
SendPacket(&data);
return;
}
if (type == 9)
{
if (objmgr.GetGuildByName(name))
{
SendGuildCommandResult(GUILD_CREATE_S, name, ERR_GUILD_NAME_EXISTS_S);
return;
}
}
else
{
if (objmgr.GetArenaTeamByName(name))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ARENA_TEAM_NAME_EXISTS_S);
return;
}
}
// and at last charter item check
Item *item = _player->GetItemByGuid(petitionguid);
if (!item)
return;
// OK!
// delete charter item
_player->DestroyItem(item->GetBagSlot(),item->GetSlot(), true);
if (type == 9) // create guild
{
Guild* guild = new Guild;
if (!guild->Create(_player, name))
{
delete guild;
return;
}
// register guild and add guildmaster
objmgr.AddGuild(guild);
// add members
for (uint8 i = 0; i < signs; ++i)
{
Field* fields = result->Fetch();
guild->AddMember(fields[0].GetUInt64(), guild->GetLowestRank());
result->NextRow();
}
}
else // or arena team
{
ArenaTeam* at = new ArenaTeam;
if (!at->Create(_player->GetGUID(), type, name))
{
sLog.outError("PetitionsHandler: arena team create failed.");
delete at;
return;
}
uint32 icon, iconcolor, border, bordercolor, backgroud;
recv_data >> backgroud >> icon >> iconcolor >> border >> bordercolor;
at->SetEmblem(backgroud, icon, iconcolor, border, bordercolor);
// register team and add captain
objmgr.AddArenaTeam(at);
sLog.outDebug("PetitonsHandler: arena team added to objmrg");
// add members
for (uint8 i = 0; i < signs; ++i)
{
Field* fields = result->Fetch();
uint64 memberGUID = fields[0].GetUInt64();
sLog.outDebug("PetitionsHandler: adding arena member %u", GUID_LOPART(memberGUID));
at->AddMember(memberGUID);
result->NextRow();
}
}
CharacterDatabase.BeginTransaction();
CharacterDatabase.PExecute("DELETE FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
CharacterDatabase.CommitTransaction();
// created
sLog.outDebug("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("Received CMSG_PETITION_SHOWLIST"); // ok
//recv_data.hexlike();
uint64 guid;
recv_data >> guid;
SendPetitionShowList(guid);
}
void WorldSession::SendPetitionShowList(uint64 guid)
{
Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_PETITIONER);
if (!pCreature)
{
sLog.outDebug("WORLD: HandlePetitionShowListOpcode - 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);
uint8 count = 0;
if (pCreature->isTabardDesigner())
count = 1;
else
count = 3;
WorldPacket data(SMSG_PETITION_SHOWLIST, 8+1+4*6);
data << guid; // npc guid
data << count; // count
if (count == 1)
{
data << uint32(1); // index
data << uint32(GUILD_CHARTER); // charter entry
data << uint32(16161); // charter display id
data << uint32(GUILD_CHARTER_COST); // charter cost
data << uint32(0); // unknown
data << uint32(9); // required signs?
}
else
{
// 2v2
data << uint32(1); // index
data << uint32(ARENA_TEAM_CHARTER_2v2); // charter entry
data << uint32(16161); // 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(16161); // 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(16161); // charter display id
data << uint32(ARENA_TEAM_CHARTER_5v5_COST); // charter cost
data << uint32(5); // unknown
data << uint32(5); // required signs?
}
//for (uint8 i = 0; i < count; ++i)
//{
// data << uint32(i); // index
// data << uint32(GUILD_CHARTER); // charter entry
// data << uint32(16161); // charter display id
// data << uint32(GUILD_CHARTER_COST+i); // charter cost
// data << uint32(0); // unknown
// data << uint32(9); // required signs?
//}
SendPacket(&data);
sLog.outDebug("Sent SMSG_PETITION_SHOWLIST");
}
@@ -0,0 +1,539 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 Trinity <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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "Common.h"
#include "Language.h"
#include "Database/DatabaseEnv.h"
#include "Database/DatabaseImpl.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(Player *p)
{
if (!p)
return;
// guess size
WorldPacket data(SMSG_NAME_QUERY_RESPONSE, (8+1+1+1+1+1+10));
data.append(p->GetPackGUID()); // player guid
data << uint8(0); // added in 3.1
data << p->GetName(); // played name
data << uint8(0); // realm name for cross realm BG usage
data << uint8(p->getRace());
data << uint8(p->getGender());
data << uint8(p->getClass());
if (DeclinedName const* names = p->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
SendPacket(&data);
}
void WorldSession::SendNameQueryOpcodeFromDB(uint64 guid)
{
CharacterDatabase.AsyncPQuery(&WorldSession::SendNameQueryOpcodeFromDBCallBack, GetAccountId(),
!sWorld.getConfig(CONFIG_DECLINED_NAMES_USED) ?
// ------- Query Without Declined Names --------
// 0 1 2 3 4
"SELECT guid, name, race, gender, class "
"FROM characters WHERE guid = '%u'"
:
// --------- Query With Declined Names ---------
// 0 1 2 3 4
"SELECT characters.guid, name, race, gender, class, "
// 5 6 7 8 9
"genitive, dative, accusative, instrumental, prepositional "
"FROM characters LEFT JOIN character_declinedname ON characters.guid = character_declinedname.guid WHERE characters.guid = '%u'",
GUID_LOPART(guid));
}
void WorldSession::SendNameQueryOpcodeFromDBCallBack(QueryResult_AutoPtr result, uint32 accountId)
{
if (!result)
return;
WorldSession * session = sWorld.FindSession(accountId);
if (!session)
return;
Field *fields = result->Fetch();
uint32 guid = fields[0].GetUInt32();
std::string name = fields[1].GetCppString();
uint8 pRace = 0, pGender = 0, pClass = 0;
if (name == "")
name = session->GetTrinityString(LANG_NON_EXIST_CHARACTER);
else
{
pRace = fields[2].GetUInt8();
pGender = fields[3].GetUInt8();
pClass = fields[4].GetUInt8();
}
// guess size
WorldPacket data(SMSG_NAME_QUERY_RESPONSE, (8+1+1+1+1+1+1+10));
data.appendPackGUID(MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER));
data << uint8(0); // added in 3.1
data << name;
data << uint8(0); // realm name for cross realm BG usage
data << uint8(pRace); // race
data << uint8(pGender); // gender
data << uint8(pClass); // class
// if the first declined name field (5) is empty, the rest must be too
if (sWorld.getConfig(CONFIG_DECLINED_NAMES_USED) && fields[5].GetCppString() != "")
{
data << uint8(1); // is declined
for (int i = 5; i < MAX_DECLINED_NAME_CASES+5; ++i)
data << fields[i].GetCppString();
}
else
data << uint8(0); // is not declined
session->SendPacket(&data);
}
void WorldSession::HandleNameQueryOpcode(WorldPacket & recv_data)
{
uint64 guid;
recv_data >> guid;
Player *pChar = objmgr.GetPlayer(guid);
if (pChar)
SendNameQueryOpcode(pChar);
else
SendNameQueryOpcodeFromDB(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 send in this packet !!!
void WorldSession::HandleCreatureQueryOpcode(WorldPacket & recv_data)
{
uint32 entry;
recv_data >> entry;
uint64 guid;
recv_data >> guid;
CreatureInfo const *ci = objmgr.GetCreatureTemplate(entry);
if (ci)
{
std::string Name, SubName;
Name = ci->Name;
SubName = ci->SubName;
int loc_idx = GetSessionDbLocaleIndex();
if (loc_idx >= 0)
{
CreatureLocale const *cl = objmgr.GetCreatureLocale(entry);
if (cl)
{
if (cl->Name.size() > size_t(loc_idx) && !cl->Name[loc_idx].empty())
Name = cl->Name[loc_idx];
if (cl->SubName.size() > size_t(loc_idx) && !cl->SubName[loc_idx].empty())
SubName = cl->SubName[loc_idx];
}
}
sLog.outDetail("WORLD: CMSG_CREATURE_QUERY '%s' - Entry: %u.", ci->Name, 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 < 6; ++i)
data << uint32(ci->questItems[i]); // itemId[6], quest drop
data << uint32(ci->movementId); // CreatureMovementInfo.dbc
SendPacket(&data);
sLog.outDebug("WORLD: Sent SMSG_CREATURE_QUERY_RESPONSE");
}
else
{
sLog.outDebug("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("WORLD: Sent SMSG_CREATURE_QUERY_RESPONSE");
}
}
/// Only _static_ data send in this packet !!!
void WorldSession::HandleGameObjectQueryOpcode(WorldPacket & recv_data)
{
uint32 entryID;
recv_data >> entryID;
uint64 guid;
recv_data >> guid;
const GameObjectInfo *info = objmgr.GetGameObjectInfo(entryID);
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)
{
GameObjectLocale const *gl = objmgr.GetGameObjectLocale(entryID);
if (gl)
{
if (gl->Name.size() > size_t(loc_idx) && !gl->Name[loc_idx].empty())
Name = gl->Name[loc_idx];
if (gl->CastBarCaption.size() > size_t(loc_idx) && !gl->CastBarCaption[loc_idx].empty())
CastBarCaption = gl->CastBarCaption[loc_idx];
}
}
sLog.outDetail("WORLD: CMSG_GAMEOBJECT_QUERY '%s' - Entry: %u. ", info->name, entryID);
WorldPacket data (SMSG_GAMEOBJECT_QUERY_RESPONSE, 150);
data << uint32(entryID);
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 < 6; ++i)
data << uint32(info->questItems[i]); // itemId[6], quest drop
SendPacket(&data);
sLog.outDebug("WORLD: Sent SMSG_GAMEOBJECT_QUERY_RESPONSE");
}
else
{
sLog.outDebug("WORLD: CMSG_GAMEOBJECT_QUERY - Missing gameobject info for (GUID: %u, ENTRY: %u)",
GUID_LOPART(guid), entryID);
WorldPacket data (SMSG_GAMEOBJECT_QUERY_RESPONSE, 4);
data << uint32(entryID | 0x80000000);
SendPacket(&data);
sLog.outDebug("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 = MapManager::Instance().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()->SetUInt64Value(UNIT_FIELD_TARGET, guid);
GossipText const* pGossip = objmgr.GetGossipText(textID);
WorldPacket data(SMSG_NPC_TEXT_UPDATE, 100); // guess size
data << textID;
if (!pGossip)
{
for (uint32 i = 0; i < 8; ++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[8], Text_1[8];
for (int i = 0; i < 8; ++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)
{
NpcTextLocale const *nl = objmgr.GetNpcTextLocale(textID);
if (nl)
{
for (int i = 0; i < 8; ++i)
{
if (nl->Text_0[i].size() > size_t(loc_idx) && !nl->Text_0[i][loc_idx].empty())
Text_0[i]=nl->Text_0[i][loc_idx];
if (nl->Text_1[i].size() > size_t(loc_idx) && !nl->Text_1[i][loc_idx].empty())
Text_1[i]=nl->Text_1[i][loc_idx];
}
}
}
for (int i = 0; i < 8; ++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 < 3; ++j)
{
data << pGossip->Options[i].Emotes[j]._Delay;
data << pGossip->Options[i].Emotes[j]._Emote;
}
}
}
SendPacket(&data);
sLog.outDebug("WORLD: Sent SMSG_NPC_TEXT_UPDATE");
}
void WorldSession::HandlePageTextQueryOpcode(WorldPacket & recv_data)
{
sLog.outDetail("WORLD: Received CMSG_PAGE_TEXT_QUERY");
recv_data.hexlike();
uint32 pageID;
recv_data >> pageID;
recv_data.read_skip<uint64>(); // guid
while (pageID)
{
PageText const *pPage = sPageTextStore.LookupEntry<PageText>(pageID);
// guess size
WorldPacket data(SMSG_PAGE_TEXT_QUERY_RESPONSE, 50);
data << pageID;
if (!pPage)
{
data << "Item page missing.";
data << uint32(0);
pageID = 0;
}
else
{
std::string Text = pPage->Text;
int loc_idx = GetSessionDbLocaleIndex();
if (loc_idx >= 0)
{
PageTextLocale const *pl = objmgr.GetPageTextLocale(pageID);
if (pl)
{
if (pl->Text.size() > size_t(loc_idx) && !pl->Text[loc_idx].empty())
Text = pl->Text[loc_idx];
}
}
data << Text;
data << uint32(pPage->Next_Page);
pageID = pPage->Next_Page;
}
SendPacket(&data);
sLog.outDebug("WORLD: Sent SMSG_PAGE_TEXT_QUERY_RESPONSE");
}
}
void WorldSession::HandleCorpseMapPositionQuery(WorldPacket & recv_data)
{
sLog.outDebug("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)
return;
WorldPacket data(SMSG_QUEST_POI_QUERY_RESPONSE, 4+(4+4)*count);
data << uint32(count); // count
for (int 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 = objmgr.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);
}
@@ -0,0 +1,721 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 Trinity <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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#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"
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("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.NPCDialogStatus(_player, cr_questgiver);
if (questStatus > 6)
questStatus = getDialogStatus(_player, cr_questgiver, defstatus);
}
break;
}
case TYPEID_GAMEOBJECT:
{
sLog.outDebug("WORLD: Received CMSG_QUESTGIVER_STATUS_QUERY for GameObject guid = %u",uint32(GUID_LOPART(guid)));
GameObject* go_questgiver=(GameObject*)questgiver;
questStatus = sScriptMgr.GODialogStatus(_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 ("WORLD: Received CMSG_QUESTGIVER_HELLO npc = %u", GUID_LOPART(guid));
Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(guid,UNIT_NPC_FLAG_NONE);
if (!pCreature)
{
sLog.outDebug ("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
pCreature->StopMoving();
if (sScriptMgr.GossipHello(_player, pCreature))
return;
_player->PrepareGossipMenu(pCreature, pCreature->GetCreatureInfo()->GossipMenuId, true);
_player->SendPreparedGossip(pCreature);
}
void WorldSession::HandleQuestgiverAcceptQuestOpcode(WorldPacket & recv_data)
{
uint64 guid;
uint32 quest;
uint32 unk1;
recv_data >> guid >> quest >> unk1;
if (!GetPlayer()->isAlive())
return;
sLog.outDebug("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->CloseGossip();
_player->SetDivider(0);
return;
}
Quest const* qInfo = objmgr.GetQuestTemplate(quest);
if (qInfo)
{
// prevent cheating
if (!GetPlayer()->CanTakeQuest(qInfo,true))
{
_player->PlayerTalkClass->CloseGossip();
_player->SetDivider(0);
return;
}
if (_player->GetDivider() != 0)
{
Player *pPlayer = ObjectAccessor::FindPlayer(_player->GetDivider());
if (pPlayer)
{
pPlayer->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* pGroup = _player->GetGroup())
{
for (GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
{
Player* pPlayer = itr->getSource();
if (!pPlayer || pPlayer == _player) // not self
continue;
if (pPlayer->CanTakeQuest(qInfo, true))
{
pPlayer->SetDivider(_player->GetGUID());
//need confirmation that any gossip window will close
pPlayer->PlayerTalkClass->CloseGossip();
_player->SendQuestConfirmAccept(qInfo, pPlayer);
}
}
}
}
if (_player->CanCompleteQuest(quest))
_player->CompleteQuest(quest);
switch(pObject->GetTypeId())
{
case TYPEID_UNIT:
sScriptMgr.QuestAccept(_player, (pObject->ToCreature()), qInfo);
break;
case TYPEID_ITEM:
case TYPEID_CONTAINER:
{
sScriptMgr.ItemQuestAccept(_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->ReqItemId[i] == ((Item*)pObject)->GetEntry()) && (((Item*)pObject)->GetProto()->MaxCount > 0))
{
destroyItem = false;
break;
}
}
if (destroyItem)
_player->DestroyItem(((Item*)pObject)->GetBagSlot(),((Item*)pObject)->GetSlot(),true);
break;
}
case TYPEID_GAMEOBJECT:
sScriptMgr.GOQuestAccept(_player, ((GameObject*)pObject), qInfo);
break;
}
_player->PlayerTalkClass->CloseGossip();
if (qInfo->GetSrcSpell() > 0)
_player->CastSpell(_player, qInfo->GetSrcSpell(), true);
return;
}
}
_player->PlayerTalkClass->CloseGossip();
}
void WorldSession::HandleQuestgiverQueryQuestOpcode(WorldPacket & recv_data)
{
uint64 guid;
uint32 quest;
uint8 unk1;
recv_data >> guid >> quest >> unk1;
sLog.outDebug("WORLD: Received CMSG_QUESTGIVER_QUERY_QUEST npc = %u, quest = %u, unk1 = %u", uint32(GUID_LOPART(guid)), quest, unk1);
// Verify that the guid is valid and is a questgiver or involved in the requested quest
Object* pObject = ObjectAccessor::GetObjectByTypeMask(*_player, guid,TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT|TYPEMASK_ITEM);
if (!pObject||!pObject->hasQuest(quest) && !pObject->hasInvolvedQuest(quest))
{
_player->PlayerTalkClass->CloseGossip();
return;
}
Quest const* pQuest = objmgr.GetQuestTemplate(quest);
if (pQuest)
{
if (pQuest->HasFlag(QUEST_FLAGS_AUTO_ACCEPT) && _player->CanAddQuest(pQuest, true))
{
_player->AddQuest(pQuest, pObject);
if (_player->CanCompleteQuest(quest))
_player->CompleteQuest(quest);
}
if (pQuest->HasFlag(QUEST_FLAGS_AUTOCOMPLETE))
_player->PlayerTalkClass->SendQuestGiverRequestItems(pQuest, pObject->GetGUID(), _player->CanCompleteQuest(pQuest->GetQuestId()), true);
else
_player->PlayerTalkClass->SendQuestGiverQuestDetails(pQuest, pObject->GetGUID(), true);
}
}
void WorldSession::HandleQuestQueryOpcode(WorldPacket & recv_data)
{
uint32 quest;
recv_data >> quest;
sLog.outDebug("WORLD: Received CMSG_QUEST_QUERY quest = %u",quest);
Quest const *pQuest = objmgr.GetQuestTemplate(quest);
if (pQuest)
{
_player->PlayerTalkClass->SendQuestQueryResponse(pQuest);
}
}
void WorldSession::HandleQuestgiverChooseRewardOpcode(WorldPacket & recv_data)
{
uint32 quest, reward;
uint64 guid;
recv_data >> guid >> quest >> 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;
}
if (!GetPlayer()->isAlive())
return;
sLog.outDebug("WORLD: Received CMSG_QUESTGIVER_CHOOSE_REWARD npc = %u, quest = %u, reward = %u",uint32(GUID_LOPART(guid)),quest,reward);
Object* pObject = ObjectAccessor::GetObjectByTypeMask(*_player, guid,TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT);
if (!pObject)
return;
if (!pObject->hasInvolvedQuest(quest))
return;
Quest const *pQuest = objmgr.GetQuestTemplate(quest);
if (pQuest)
{
if (_player->CanRewardQuest(pQuest, reward, true))
{
_player->RewardQuest(pQuest, reward, pObject);
switch(pObject->GetTypeId())
{
case TYPEID_UNIT:
if (!(sScriptMgr.ChooseReward(_player, (pObject->ToCreature()), pQuest, reward)))
{
// Send next quest
if (Quest const* nextquest = _player->GetNextQuest(guid ,pQuest))
_player->PlayerTalkClass->SendQuestGiverQuestDetails(nextquest,guid,true);
}
break;
case TYPEID_GAMEOBJECT:
if (!sScriptMgr.GOChooseReward(_player, ((GameObject*)pObject), pQuest, reward))
{
// Send next quest
if (Quest const* nextquest = _player->GetNextQuest(guid ,pQuest))
_player->PlayerTalkClass->SendQuestGiverQuestDetails(nextquest,guid,true);
}
break;
}
}
else
_player->PlayerTalkClass->SendQuestGiverOfferReward(pQuest, guid, true);
}
}
void WorldSession::HandleQuestgiverRequestRewardOpcode(WorldPacket & recv_data)
{
uint32 quest;
uint64 guid;
recv_data >> guid >> quest;
if (!GetPlayer()->isAlive())
return;
sLog.outDebug("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;
if (_player->CanCompleteQuest(quest))
_player->CompleteQuest(quest);
if (_player->GetQuestStatus(quest) != QUEST_STATUS_COMPLETE)
return;
if (Quest const *pQuest = objmgr.GetQuestTemplate(quest))
_player->PlayerTalkClass->SendQuestGiverOfferReward(pQuest, guid, true);
}
void WorldSession::HandleQuestgiverCancel(WorldPacket& /*recv_data*/)
{
sLog.outDebug("WORLD: Received CMSG_QUESTGIVER_CANCEL");
_player->PlayerTalkClass->CloseGossip();
}
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("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("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 = objmgr.GetQuestTemplate(quest))
{
if (pQuest->HasFlag(QUEST_TRINITY_FLAGS_TIMED))
_player->RemoveTimedQuest(quest);
}
_player->TakeQuestSourceItem(quest, true); // remove quest src item from player
_player->SetQuestStatus(quest, QUEST_STATUS_NONE);
_player->GetAchievementMgr().RemoveTimedAchievement(ACHIEVEMENT_TIMED_TYPE_QUEST, 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("WORLD: Received CMSG_QUEST_CONFIRM_ACCEPT quest = %u", quest);
if (const Quest* pQuest = objmgr.GetQuestTemplate(quest))
{
if (!pQuest->HasFlag(QUEST_FLAGS_PARTY_ACCEPT))
return;
Player* pOriginalPlayer = ObjectAccessor::FindPlayer(_player->GetDivider());
if (!pOriginalPlayer)
return;
if (pQuest->GetType() == QUEST_TYPE_RAID)
{
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;
if (!GetPlayer()->isAlive())
return;
sLog.outDebug("WORLD: Received CMSG_QUESTGIVER_COMPLETE_QUEST npc = %u, quest = %u",uint32(GUID_LOPART(guid)),quest);
Quest const *pQuest = objmgr.GetQuestTemplate(quest);
if (pQuest)
{
// TODO: need a virtual function
if (GetPlayer()->InBattleGround())
if (BattleGround* bg = GetPlayer()->GetBattleGround())
if (bg->GetTypeID() == BATTLEGROUND_AV)
((BattleGroundAV*)bg)->HandleQuestComplete(quest, GetPlayer());
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("WORLD: Received CMSG_QUESTGIVER_QUEST_AUTOLAUNCH");
}
void WorldSession::HandlePushQuestToParty(WorldPacket& recvPacket)
{
uint32 questId;
recvPacket >> questId;
sLog.outDebug("WORLD: Received CMSG_PUSHQUESTTOPARTY quest = %u", questId);
if (Quest const *pQuest = objmgr.GetQuestTemplate(questId))
{
if (Group* pGroup = _player->GetGroup())
{
for (GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
{
Player *pPlayer = itr->getSource();
if (!pPlayer || pPlayer == _player) // skip self
continue;
_player->SendPushToPartyResponse(pPlayer, QUEST_PARTY_MSG_SHARING_QUEST);
if (!pPlayer->SatisfyQuestStatus(pQuest, false))
{
_player->SendPushToPartyResponse(pPlayer, QUEST_PARTY_MSG_HAVE_QUEST);
continue;
}
if (pPlayer->GetQuestStatus(questId) == QUEST_STATUS_COMPLETE)
{
_player->SendPushToPartyResponse(pPlayer, QUEST_PARTY_MSG_FINISH_QUEST);
continue;
}
if (!pPlayer->CanTakeQuest(pQuest, false))
{
_player->SendPushToPartyResponse(pPlayer, QUEST_PARTY_MSG_CANT_TAKE_QUEST);
continue;
}
if (!pPlayer->SatisfyQuestLog(false))
{
_player->SendPushToPartyResponse(pPlayer, QUEST_PARTY_MSG_LOG_FULL);
continue;
}
if (pPlayer->GetDivider() != 0)
{
_player->SendPushToPartyResponse(pPlayer, QUEST_PARTY_MSG_BUSY);
continue;
}
pPlayer->PlayerTalkClass->SendQuestGiverQuestDetails(pQuest, _player->GetGUID(), true);
pPlayer->SetDivider(_player->GetGUID());
}
}
}
}
void WorldSession::HandleQuestPushResult(WorldPacket& recvPacket)
{
uint64 guid;
uint8 msg;
recvPacket >> guid >> msg;
sLog.outDebug("WORLD: Received MSG_QUEST_PUSH_RESULT");
if (_player->GetDivider() != 0)
{
Player *pPlayer = ObjectAccessor::FindPlayer(_player->GetDivider());
if (pPlayer)
{
WorldPacket data(MSG_QUEST_PUSH_RESULT, (8+1));
data << uint64(guid);
data << uint8(msg); // valid values: 0-8
pPlayer->GetSession()->SendPacket(&data);
_player->SetDivider(0);
}
}
}
uint32 WorldSession::getDialogStatus(Player *pPlayer, Object* questgiver, uint32 defstatus)
{
uint32 result = defstatus;
QuestRelations const* qir;
QuestRelations const* qr;
switch(questgiver->GetTypeId())
{
case TYPEID_GAMEOBJECT:
{
qir = &objmgr.mGOQuestInvolvedRelations;
qr = &objmgr.mGOQuestRelations;
break;
}
case TYPEID_UNIT:
{
qir = &objmgr.mCreatureQuestInvolvedRelations;
qr = &objmgr.mCreatureQuestRelations;
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->lower_bound(questgiver->GetEntry()); i != qir->upper_bound(questgiver->GetEntry()); ++i)
{
uint32 result2 = 0;
uint32 quest_id = i->second;
Quest const *pQuest = objmgr.GetQuestTemplate(quest_id);
if (!pQuest) continue;
QuestStatus status = pPlayer->GetQuestStatus(quest_id);
if ((status == QUEST_STATUS_COMPLETE && !pPlayer->GetQuestRewardStatus(quest_id)) ||
(pQuest->IsAutoComplete() && pPlayer->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->lower_bound(questgiver->GetEntry()); i != qr->upper_bound(questgiver->GetEntry()); ++i)
{
uint32 result2 = 0;
uint32 quest_id = i->second;
Quest const *pQuest = objmgr.GetQuestTemplate(quest_id);
if (!pQuest)
continue;
QuestStatus status = pPlayer->GetQuestStatus(quest_id);
if (status == QUEST_STATUS_NONE)
{
if (pPlayer->CanSeeStartQuest(pQuest))
{
if (pPlayer->SatisfyQuestLevel(pQuest, false))
{
if (pQuest->IsAutoComplete() || (pQuest->IsRepeatable() && pPlayer->getQuestStatusMap()[quest_id].m_rewarded))
result2 = DIALOG_STATUS_REWARD_REP;
else if (pPlayer->getLevel() <= ((pPlayer->GetQuestLevel(pQuest) == -1) ? pPlayer->getLevel() : pPlayer->GetQuestLevel(pQuest) + sWorld.getConfig(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("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.NPCDialogStatus(_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.GODialogStatus(_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*/)
{
uint32 count = 0;
WorldPacket data(SMSG_QUERY_QUESTS_COMPLETED_RESPONSE, 4+4*count);
data << uint32(count);
for (QuestStatusMap::const_iterator itr = _player->getQuestStatusMap().begin(); itr != _player->getQuestStatusMap().end(); ++itr)
{
if (itr->second.m_rewarded)
{
data << uint32(itr->first);
count++;
}
}
data.put<uint32>(0, count);
SendPacket(&data);
}
@@ -0,0 +1,95 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 Trinity <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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "Common.h"
#include "Database/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("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.outDetail("MSG_TALENT_WIPE_CONFIRM");
uint64 guid;
recv_data >> guid;
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(guid,UNIT_NPC_FLAG_TRAINER);
if (!unit)
{
sLog.outDebug("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);
}
@@ -0,0 +1,649 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 Trinity <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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#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"
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;
uint8 unk_flags; // flags (if 0x02 - some additional data are received)
uint8 cast_count; // next cast if exists (single or not)
uint64 item_guid;
uint32 glyphIndex; // something to do with glyphs?
uint32 spellid; // casted spell id
recvPacket >> bagIndex >> slot >> cast_count >> spellid >> item_guid >> glyphIndex >> unk_flags;
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() != item_guid)
{
pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL);
return;
}
sLog.outDetail("WORLD: CMSG_USE_ITEM packet, bagIndex: %u, slot: %u, cast_count: %u, spellid: %u, Item: %u, glyphIndex: %u, unk_flags: %u, data length = %i", bagIndex, slot, cast_count, spellid, pItem->GetEntry(), glyphIndex, unk_flags, (uint32)recvPacket.size());
ItemPrototype const *proto = pItem->GetProto();
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;
}
uint8 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_FLAGS_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 (SpellEntry const *spellInfo = sSpellStore.LookupEntry(proto->Spells[i].SpellId))
{
if (IsNonCombatSpell(spellInfo))
{
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->GetProto()->Bonding == BIND_WHEN_USE || pItem->GetProto()->Bonding == BIND_WHEN_PICKED_UP || pItem->GetProto()->Bonding == BIND_QUEST_ITEM)
{
if (!pItem->IsSoulBound())
{
pItem->SetState(ITEM_CHANGED, pUser);
pItem->SetBinding(true);
}
}
SpellCastTargets targets;
if (!targets.read(&recvPacket, pUser))
return;
targets.Update(pUser);
if (!pItem->IsTargetValidForItemUse(targets.getUnitTarget()))
{
// free gray item after use fail
pUser->SendEquipError(EQUIP_ERR_NONE, pItem, NULL);
// send spell error
if (SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellid))
{
// for implicit area/coord target spells
if (!targets.getUnitTarget())
Spell::SendCastResult(_player,spellInfo,cast_count,SPELL_FAILED_NO_VALID_TARGETS);
// for explicit target spells
else
Spell::SendCastResult(_player,spellInfo,cast_count,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.ItemUse(pUser,pItem,targets))
{
// no script or script not process request by self
pUser->CastItemUseSpell(pItem,targets,cast_count,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 *pItem = pUser->GetItemByPos(bagIndex, slot);
if (!pItem)
{
pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL);
return;
}
ItemPrototype const *proto = pItem->GetProto();
if (!proto)
{
pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL);
return;
}
if (!pUser->GetSession()->HandleOnItemOpen(pItem))
return;
// locked item
uint32 lockId = proto->LockID;
if (lockId)
{
LockEntry const *lockInfo = sLockStore.LookupEntry(lockId);
if (!lockInfo)
{
pUser->SendEquipError(EQUIP_ERR_ITEM_LOCKED, pItem, NULL);
sLog.outError("WORLD::OpenItem: item [guid = %u] has an unknown lockId: %u!", pItem->GetGUIDLow(), lockId);
return;
}
// required picklocking
if (lockInfo->Skill[1] || lockInfo->Skill[0])
{
pUser->SendEquipError(EQUIP_ERR_ITEM_LOCKED, pItem, NULL);
return;
}
}
if (pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED))// wrapped?
{
QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT entry, flags FROM character_gifts WHERE item_guid = '%u'", pItem->GetGUIDLow());
if (result)
{
Field *fields = result->Fetch();
uint32 entry = fields[0].GetUInt32();
uint32 flags = fields[1].GetUInt32();
pItem->SetUInt64Value(ITEM_FIELD_GIFTCREATOR, 0);
pItem->SetEntry(entry);
pItem->SetUInt32Value(ITEM_FIELD_FLAGS, flags);
pItem->SetState(ITEM_CHANGED, pUser);
}
else
{
sLog.outError("Wrapped item %u don't have record in character_gifts table and will deleted", pItem->GetGUIDLow());
pUser->DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), true);
return;
}
CharacterDatabase.PExecute("DELETE FROM character_gifts WHERE item_guid = '%u'", pItem->GetGUIDLow());
}
else
pUser->SendLoot(pItem->GetGUID(),LOOT_CORPSE);
}
void WorldSession::HandleGameObjectUseOpcode(WorldPacket & recv_data)
{
uint64 guid;
recv_data >> guid;
sLog.outDebug("WORLD: Recvd CMSG_GAMEOBJ_USE Message [guid=%u]", GUID_LOPART(guid));
// ignore for remote control state
if (_player->m_mover != _player)
return;
GameObject *obj = GetPlayer()->GetMap()->GetGameObject(guid);
if (!obj)
return;
if (sScriptMgr.GOHello(_player, obj))
return;
obj->Use(_player);
}
void WorldSession::HandleGameobjectReportUse(WorldPacket& recvPacket)
{
uint64 guid;
recvPacket >> guid;
sLog.outDebug("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;
_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT, go->GetEntry());
}
void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket)
{
uint32 spellId;
uint8 cast_count, unk_flags;
recvPacket >> cast_count;
recvPacket >> spellId;
recvPacket >> unk_flags; // flags (if 0x02 - some additional data are received)
// ignore for remote control state (for player case)
Unit* mover = _player->m_mover;
if (mover != _player && mover->GetTypeId() == TYPEID_PLAYER)
{
recvPacket.rpos(recvPacket.wpos()); // prevent spam at ignore packet
return;
}
sLog.outDebug("WORLD: got cast spell packet, spellId - %u, cast_count: %u, unk_flags %u, data length = %i", spellId, cast_count, unk_flags, (uint32)recvPacket.size());
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
if (!spellInfo)
{
sLog.outError("WORLD: unknown spell id %u", spellId);
recvPacket.rpos(recvPacket.wpos()); // 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) || IsPassiveSpell(spellId))
{
//cheater? kick? ban?
recvPacket.rpos(recvPacket.wpos()); // 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)) || IsPassiveSpell(spellId))
{
//cheater? kick? ban?
recvPacket.rpos(recvPacket.wpos()); // 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 (IsAutoRepeatRangedSpell(spellInfo) && _player->GetCurrentSpell(CURRENT_AUTOREPEAT_SPELL)
&& _player->GetCurrentSpell(CURRENT_AUTOREPEAT_SPELL)->m_spellInfo == spellInfo)
return;
// can't use our own spells when we're in possession of another unit,
if (_player->isPossessing())
return;
// client provided targets
SpellCastTargets targets;
if (!targets.read(&recvPacket,mover))
{
recvPacket.rpos(recvPacket.wpos()); // prevent spam at ignore packet
return;
}
// some spell cast packet including more data (for projectiles?)
if (unk_flags & 0x02)
{
//recvPacket.read_skip<float>(); // unk1, coords?
//recvPacket.read_skip<float>(); // unk1, coords?
uint8 unk1;
recvPacket >> unk1; // >> 1 or 0
if (unk1)
{
recvPacket.read_skip<uint32>(); // >> MSG_MOVE_STOP
uint64 guid; // guid - unused
if (!recvPacket.readPackGUID(guid))
return;
MovementInfo movementInfo;
ReadMovementInfo(recvPacket, &movementInfo);
}
}
// auto-selection buff level base at target level (in spellInfo)
if (targets.getUnitTarget())
{
SpellEntry const *actualSpellInfo = spellmgr.SelectAuraRankForPlayerLevel(spellInfo,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, false);
spell->m_cast_count = cast_count; // 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;
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
if (!spellInfo)
return;
// not allow remove non positive spells and spells with attr SPELL_ATTR_CANT_CANCEL
if (!IsPositiveSpell(spellId) || (spellInfo->Attributes & SPELL_ATTR_CANT_CANCEL))
return;
// channeled spell case (it currently casted then)
if (IsChanneledSpell(spellInfo))
{
if (Spell* curSpell = _player->GetCurrentSpell(CURRENT_CHANNELED_SPELL))
if (curSpell->m_spellInfo->Id == spellId)
_player->InterruptSpell(CURRENT_CHANNELED_SPELL);
return;
}
// non channeled case
// 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;
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
if (!spellInfo)
{
sLog.outError("WORLD: unknown PET spell id %u", spellId);
return;
}
Creature* pet=ObjectAccessor::GetCreatureOrPetOrVehicle(*_player,guid);
if (!pet)
{
sLog.outError("Pet %u not exist.", uint32(GUID_LOPART(guid)));
return;
}
if (pet != GetPlayer()->GetGuardianPet() && pet != GetPlayer()->GetCharm())
{
sLog.outError("HandlePetCancelAura.Pet %u isn't 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("WORLD: CMSG_SELF_RES"); // empty opcode
if (_player->GetUInt32Value(PLAYER_SELF_RES_SPELL))
{
SpellEntry const *spellInfo = sSpellStore.LookupEntry(_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())
{
sLog.outCrash("Spell click target %u is not in world!", unit->GetEntry());
assert(false);
return;
}
SpellClickInfoMapBounds clickPair = objmgr.GetSpellClickInfoMapBounds(unit->GetEntry());
for (SpellClickInfoMap::const_iterator itr = clickPair.first; itr != clickPair.second; ++itr)
{
if (itr->second.IsFitToRequirements(_player, unit))
{
Unit *caster = (itr->second.castFlags & NPC_CLICK_CAST_CASTER_PLAYER) ? (Unit*)_player : (Unit*)unit;
Unit *target = (itr->second.castFlags & NPC_CLICK_CAST_TARGET_PLAYER) ? (Unit*)_player : (Unit*)unit;
uint64 origCasterGUID = (itr->second.castFlags & NPC_CLICK_CAST_ORIG_CASTER_OWNER) ? unit->GetOwnerGUID() : 0;
caster->CastSpell(target, itr->second.spellId, true, NULL, NULL, origCasterGUID);
}
}
if (unit->IsVehicle())
{
if (unit->CheckPlayerCondition(_player))
_player->EnterVehicle(unit);
}
unit->AI()->DoAction(EVENT_SPELLCLICK);
}
void WorldSession::HandleMirrrorImageDataRequest(WorldPacket & recv_data)
{
sLog.outDebug("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;
// Get creator of the unit
Unit *creator = ObjectAccessor::GetObjectInWorld(unit->GetCreatorGUID(),(Unit*)NULL);
if (!creator)
return;
WorldPacket data(SMSG_MIRRORIMAGE_DATA, 68);
data << uint64(guid);
data << uint32(creator->GetDisplayId());
if (creator->GetTypeId() == TYPEID_PLAYER)
{
Player * pCreator = creator->ToPlayer();
data << uint8(pCreator->getRace());
data << uint8(pCreator->getGender());
data << uint8(pCreator->getClass());
data << uint8(pCreator->GetByteValue(PLAYER_BYTES, 0)); // skin
data << uint8(pCreator->GetByteValue(PLAYER_BYTES, 1)); // face
data << uint8(pCreator->GetByteValue(PLAYER_BYTES, 2)); // hair
data << uint8(pCreator->GetByteValue(PLAYER_BYTES, 3)); // haircolor
data << uint8(pCreator->GetByteValue(PLAYER_BYTES_2, 0)); // facialhair
data << uint32(pCreator->GetGuildId()); // unk
static const EquipmentSlots 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 && pCreator->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM))
data << uint32(0);
else if (*itr == EQUIPMENT_SLOT_BACK && pCreator->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK))
data << uint32(0);
else if (Item const *item = pCreator->GetItemByPos(INVENTORY_SLOT_BAG_0, *itr))
data << uint32(item->GetProto()->DisplayInfoID);
else
data << uint32(0);
}
}
else
{
// Skip player data for creatures
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);
data << uint32(0);
}
SendPacket(&data);
}
@@ -0,0 +1,287 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 Trinity <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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "Common.h"
#include "Database/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"
#include "DestinationHolderImp.h"
void WorldSession::HandleTaxiNodeStatusQueryOpcode(WorldPacket & recv_data)
{
sLog.outDebug("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("WorldSession::SendTaxiStatus - Unit (GUID: %u) not found.", uint32(GUID_LOPART(guid)));
return;
}
uint32 curloc = objmgr.GetNearestTaxiNode(unit->GetPositionX(),unit->GetPositionY(),unit->GetPositionZ(),unit->GetMapId(),GetPlayer()->GetTeam());
// not found nearest
if (curloc == 0)
return;
sLog.outDebug("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("WORLD: Sent SMSG_TAXINODE_STATUS");
}
void WorldSession::HandleTaxiQueryAvailableNodes(WorldPacket & recv_data)
{
sLog.outDebug("WORLD: Received CMSG_TAXIQUERYAVAILABLENODES");
uint64 guid;
recv_data >> guid;
// cheating checks
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_FLIGHTMASTER);
if (!unit)
{
sLog.outDebug("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 = objmgr.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("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("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 = objmgr.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::HandleActivateTaxiExpressOpcode (WorldPacket & recv_data)
{
sLog.outDebug("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("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("WORLD: Received CMSG_ACTIVATETAXIEXPRESS from %d to %d" ,nodes.front(),nodes.back());
GetPlayer()->ActivateTaxiPathTo(nodes, npc);
}
void WorldSession::HandleMoveSplineDoneOpcode(WorldPacket& recv_data)
{
sLog.outDebug("WORLD: Received CMSG_MOVE_SPLINE_DONE");
uint64 guid; // used only for proper packet read
if (!recv_data.readPackGUID(guid))
return;
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();
Path::PathNode 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("WORLD: Taxi has to go from %u to %u", sourcenode, destinationnode);
uint32 mountDisplayId = objmgr.GetTaxiMountDisplayId(sourcenode, GetPlayer()->GetTeam());
uint32 path, cost;
objmgr.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;
}
GetPlayer()->CleanupAfterTaxiFlight();
GetPlayer()->SetFallInformation(0, GetPlayer()->GetPositionZ());
if (GetPlayer()->pvpInfo.inHostileArea)
GetPlayer()->CastSpell(GetPlayer(), 2479, true);
}
void WorldSession::HandleActivateTaxiOpcode(WorldPacket & recv_data)
{
sLog.outDebug("WORLD: Received CMSG_ACTIVATETAXI");
uint64 guid;
std::vector<uint32> nodes;
nodes.resize(2);
recv_data >> guid >> nodes[0] >> nodes[1];
sLog.outDebug("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("WORLD: HandleActivateTaxiOpcode - Unit (GUID: %u) not found or you can't interact with it.", uint32(GUID_LOPART(guid)));
return;
}
GetPlayer()->ActivateTaxiPathTo(nodes, npc);
}
@@ -0,0 +1,161 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 Trinity <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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "Language.h"
#include "WorldPacket.h"
#include "Common.h"
#include "ObjectMgr.h"
#include "Player.h"
#include "World.h"
void WorldSession::HandleGMTicketCreateOpcode(WorldPacket & recv_data)
{
if (GetPlayer()->getLevel() < sWorld.getConfig(CONFIG_TICKET_LEVEL_REQ))
{
SendNotification(GetTrinityString(LANG_TICKET_REQ), sWorld.getConfig(CONFIG_TICKET_LEVEL_REQ));
return;
}
if (GM_Ticket *ticket = objmgr.GetGMTicketByPlayer(GetPlayer()->GetGUID()))
{
WorldPacket data(SMSG_GMTICKET_CREATE, 4);
data << uint32(1); // 1 - You already have GM ticket
SendPacket(&data);
return;
}
uint32 map;
float x, y, z;
std::string ticketText, ticketText2;
SendQueryTimeResponse();
WorldPacket data(SMSG_GMTICKET_CREATE, 4);
recv_data >> map;
recv_data >> x;
recv_data >> y;
recv_data >> z;
recv_data >> ticketText;
recv_data >> ticketText2;
GM_Ticket *ticket = new GM_Ticket;
ticket->name = GetPlayer()->GetName();
ticket->guid = objmgr.GenerateGMTicketId();
ticket->playerGuid = GetPlayer()->GetGUID();
ticket->message = ticketText;
ticket->createtime = time(NULL);
ticket->map = map;
ticket->pos_x = x;
ticket->pos_y = y;
ticket->pos_z = z;
ticket->timestamp = time(NULL);
ticket->closed = 0;
ticket->assignedToGM = 0;
ticket->comment = "";
objmgr.AddOrUpdateGMTicket(*ticket, true);
data << uint32(2);
SendPacket(&data);
sWorld.SendGMText(LANG_COMMAND_TICKETNEW, GetPlayer()->GetName(), ticket->guid);
}
void WorldSession::HandleGMTicketUpdateOpcode(WorldPacket & recv_data)
{
WorldPacket data(SMSG_GMTICKET_UPDATETEXT, 4);
std::string message;
recv_data >> message;
GM_Ticket *ticket = objmgr.GetGMTicketByPlayer(GetPlayer()->GetGUID());
if (!ticket)
{
data << uint32(1);
SendPacket(&data);
return;
}
ticket->message = message;
ticket->timestamp = time(NULL);
objmgr.AddOrUpdateGMTicket(*ticket);
data << uint32(2);
SendPacket(&data);
sWorld.SendGMText(LANG_COMMAND_TICKETUPDATED, GetPlayer()->GetName(), ticket->guid);
}
void WorldSession::HandleGMTicketDeleteOpcode(WorldPacket & /*recv_data*/)
{
GM_Ticket* ticket = objmgr.GetGMTicketByPlayer(GetPlayer()->GetGUID());
if (ticket)
{
WorldPacket data(SMSG_GMTICKET_DELETETICKET, 4);
data << uint32(9);
SendPacket(&data);
sWorld.SendGMText(LANG_COMMAND_TICKETPLAYERABANDON, GetPlayer()->GetName(), ticket->guid);
objmgr.RemoveGMTicket(ticket, GetPlayer()->GetGUID(), false);
SendGMTicketGetTicket(0x0A, 0);
}
}
void WorldSession::HandleGMTicketGetTicketOpcode(WorldPacket & /*recv_data*/)
{
SendQueryTimeResponse();
GM_Ticket *ticket = objmgr.GetGMTicketByPlayer(GetPlayer()->GetGUID());
if (ticket)
SendGMTicketGetTicket(0x06, ticket->message.c_str());
else
SendGMTicketGetTicket(0x0A, 0);
}
void WorldSession::HandleGMTicketSystemStatusOpcode(WorldPacket & /*recv_data*/)
{
WorldPacket data(SMSG_GMTICKET_SYSTEMSTATUS, 4);
data << uint32(1);
SendPacket(&data);
}
void WorldSession::SendGMTicketGetTicket(uint32 status, char const* text)
{
int len = text ? strlen(text) : 0;
WorldPacket data(SMSG_GMTICKET_GETTICKET, (4+len+1+4+2+4+4));
data << uint32(status); // standard 0x0A, 0x06 if text present
data << uint32(1); // unk flags, if 0, can't edit the ticket
if (status == 6)
{
data << text; // ticket text
data << uint8(0x7); // ticket category
data << float(0); // tickets in queue?
data << float(0); // if > "tickets in queue" then "We are currently experiencing a high volume of petitions."
data << float(0); // 0 - "Your ticket will be serviced soon", 1 - "Wait time currently unavailable"
data << uint8(0); // if == 2 and next field == 1 then "Your ticket has been escalated"
data << uint8(0); // const
}
SendPacket(&data);
}
@@ -0,0 +1,656 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 Trinity <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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#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 "SocialMgr.h"
#include "Language.h"
enum TradeStatus
{
TRADE_STATUS_BUSY = 0,
TRADE_STATUS_BEGIN_TRADE = 1,
TRADE_STATUS_OPEN_WINDOW = 2,
TRADE_STATUS_TRADE_CANCELED = 3,
TRADE_STATUS_TRADE_ACCEPT = 4,
TRADE_STATUS_BUSY_2 = 5,
TRADE_STATUS_NO_TARGET = 6,
TRADE_STATUS_BACK_TO_TRADE = 7,
TRADE_STATUS_TRADE_COMPLETE = 8,
// 9?
TRADE_STATUS_TARGET_TO_FAR = 10,
TRADE_STATUS_WRONG_FACTION = 11,
TRADE_STATUS_CLOSE_WINDOW = 12,
// 13?
TRADE_STATUS_IGNORE_YOU = 14,
TRADE_STATUS_YOU_STUNNED = 15,
TRADE_STATUS_TARGET_STUNNED = 16,
TRADE_STATUS_YOU_DEAD = 17,
TRADE_STATUS_TARGET_DEAD = 18,
TRADE_STATUS_YOU_LOGOUT = 19,
TRADE_STATUS_TARGET_LOGOUT = 20,
TRADE_STATUS_TRIAL_ACCOUNT = 21, // Trial accounts can not perform that action
TRADE_STATUS_ONLY_CONJURED = 22 // You can only trade conjured items... (cross realm BG related).
};
void WorldSession::SendTradeStatus(uint32 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:
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("WORLD: Ignore Trade %u",_player->GetGUIDLow());
// recvPacket.print_storage();
}
void WorldSession::HandleBusyTradeOpcode(WorldPacket& /*recvPacket*/)
{
sLog.outDebug("WORLD: Busy Trade %u",_player->GetGUIDLow());
// recvPacket.print_storage();
}
void WorldSession::SendUpdateTrade()
{
Item *item = NULL;
if (!_player || !_player->pTrader)
return;
// reset trade status
if (_player->acceptTrade)
{
_player->acceptTrade = false;
SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
}
if (_player->pTrader->acceptTrade)
{
_player->pTrader->acceptTrade = false;
_player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
}
WorldPacket data(SMSG_TRADE_STATUS_EXTENDED, (100)); // guess size
data << (uint8) 1; // can be different (only seen 0 and 1)
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) _player->pTrader->tradeGold; // trader gold
data << (uint32) 0; // spell casted on lowest slot item
for (uint8 i = 0; i < TRADE_SLOT_COUNT; ++i)
{
item = (_player->pTrader->tradeItems[i] != 0 ? _player->pTrader->GetItemByGuid(_player->pTrader->tradeItems[i]) : NULL);
data << (uint8) i; // trade slot number, if not specified, then end of packet
if (item)
{
data << (uint32) item->GetProto()->ItemId; // entry
// display id
data << (uint32) item->GetProto()->DisplayInfoID;
// stack count
data << (uint32) item->GetUInt32Value(ITEM_FIELD_STACK_COUNT);
data << (uint32) 0; // probably gift=1, created_by=0?
// gift creator
data << (uint64) item->GetUInt64Value(ITEM_FIELD_GIFTCREATOR);
data << (uint32) item->GetEnchantmentId(PERM_ENCHANTMENT_SLOT);
for (uint8 j = 0; j < 3; ++j)
data << (uint32) 0; // enchantment id (permanent/gems?)
// creator
data << (uint64) item->GetUInt64Value(ITEM_FIELD_CREATOR);
data << (uint32) item->GetSpellCharges(); // charges
data << (uint32) item->GetItemSuffixFactor(); // SuffixFactor
// random properties id
data << (int32) item->GetItemRandomPropertyId();
data << (uint32) item->GetProto()->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[])
{
for (int i=0; i<TRADE_SLOT_TRADED_COUNT; ++i)
{
ItemPosCountVec traderDst;
ItemPosCountVec playerDst;
bool traderCanTrade = (myItems[i] == NULL || _player->pTrader->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("partner storing: %u",myItems[i]->GetGUIDLow());
if (_player->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(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]->GetProto()->Name1,myItems[i]->GetEntry(),myItems[i]->GetCount(),
_player->pTrader->GetName(),_player->pTrader->GetSession()->GetAccountId());
}
// store
_player->pTrader->MoveItemToInventory(traderDst, myItems[i], true, true);
}
if (hisItems[i])
{
// logging
sLog.outDebug("player storing: %u",hisItems[i]->GetGUIDLow());
if (_player->pTrader->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE))
{
sLog.outCommand(_player->pTrader->GetSession()->GetAccountId(),"GM %s (Account: %u) trade: %s (Entry: %d Count: %u) to player: %s (Account: %u)",
_player->pTrader->GetName(),_player->pTrader->GetSession()->GetAccountId(),
hisItems[i]->GetProto()->Name1,hisItems[i]->GetEntry(),hisItems[i]->GetCount(),
_player->GetName(),_player->GetSession()->GetAccountId());
}
// 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 (_player->pTrader->CanStoreItem(NULL_BAG, NULL_SLOT, traderDst, hisItems[i], false) == EQUIP_ERR_OK)
_player->pTrader->MoveItemToInventory(traderDst, hisItems[i], true, true);
else
sLog.outError("trader can't take item back: %u",hisItems[i]->GetGUIDLow());
}
}
}
}
//==============================================================
void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/)
{
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;
if (!GetPlayer()->pTrader)
return;
// not accept case incorrect money amount
if (_player->tradeGold > _player->GetMoney())
{
SendNotification(LANG_NOT_ENOUGH_GOLD);
_player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
_player->acceptTrade = false;
return;
}
// not accept case incorrect money amount
if (_player->pTrader->tradeGold > _player->pTrader->GetMoney())
{
_player->pTrader->GetSession()->SendNotification(LANG_NOT_ENOUGH_GOLD);
SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
_player->pTrader->acceptTrade = false;
return;
}
// not accept if some items now can't be trade (cheating)
for (int i=0; i<TRADE_SLOT_TRADED_COUNT; ++i)
{
if (_player->tradeItems[i] != 0)
{
if (Item* item =_player->GetItemByGuid(_player->tradeItems[i]))
{
if (!item->CanBeTraded())
{
SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
return;
}
}
}
if (_player->pTrader->tradeItems[i] != 0)
{
if (Item* item =_player->pTrader->GetItemByGuid(_player->pTrader->tradeItems[i]))
{
if (!item->CanBeTraded())
{
SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
return;
}
}
}
}
_player->acceptTrade = true;
if (_player->pTrader->acceptTrade)
{
// inform partner client
_player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_ACCEPT);
// store items in local list and set 'in-trade' flag
for (int i=0; i<TRADE_SLOT_TRADED_COUNT; ++i)
{
if (_player->tradeItems[i] != 0)
{
//Can return NULL
myItems[i] = _player->GetItemByGuid(_player->tradeItems[i]);
if (myItems[i])
{
myItems[i]->SetInTrade();
sLog.outDebug("Player trade item bag: %u slot: %u", myItems[i]->GetBagSlot(), myItems[i]->GetSlot());
}
}
if (_player->pTrader->tradeItems[i] != 0)
{
//Can return NULL
hisItems[i]=_player->pTrader->GetItemByGuid(_player->pTrader->tradeItems[i]);
if (hisItems[i])
{
hisItems[i]->SetInTrade();
sLog.outDebug("Player trade item bag: %u slot: %u", hisItems[i]->GetBagSlot(), hisItems[i]->GetSlot());
}
}
}
// test if item will fit in each inventory
hisCanCompleteTrade = (_player->pTrader->CanStoreItems(myItems,TRADE_SLOT_TRADED_COUNT) == EQUIP_ERR_OK);
myCanCompleteTrade = (_player->CanStoreItems(hisItems,TRADE_SLOT_TRADED_COUNT) == EQUIP_ERR_OK);
// clear 'in-trade' flag
for (int i=0; i<TRADE_SLOT_TRADED_COUNT; ++i)
{
if (myItems[i]) myItems[i]->SetInTrade(false);
if (hisItems[i]) hisItems[i]->SetInTrade(false);
}
// in case of missing space report error
if (!myCanCompleteTrade)
{
SendNotification(LANG_NOT_FREE_TRADE_SLOTS);
GetPlayer()->pTrader->GetSession()->SendNotification(LANG_NOT_PARTNER_FREE_TRADE_SLOTS);
SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
_player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
return;
}
else if (!hisCanCompleteTrade)
{
SendNotification(LANG_NOT_PARTNER_FREE_TRADE_SLOTS);
GetPlayer()->pTrader->GetSession()->SendNotification(LANG_NOT_FREE_TRADE_SLOTS);
SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
_player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
return;
}
// execute trade: 1. remove
for (int i=0; i<TRADE_SLOT_TRADED_COUNT; ++i)
{
Item* iPtr = NULL;
if (myItems[i])
{
myItems[i]->SetUInt64Value(ITEM_FIELD_GIFTCREATOR, _player->GetGUID());
iPtr = _player->GetItemByGuid(_player->tradeItems[i]);
_player->MoveItemFromInventory(iPtr->GetBagSlot(), iPtr->GetSlot(), true);
}
if (hisItems[i])
{
hisItems[i]->SetUInt64Value(ITEM_FIELD_GIFTCREATOR,_player->pTrader->GetGUID());
iPtr = _player->pTrader->GetItemByGuid(_player->pTrader->tradeItems[i]);
_player->pTrader->MoveItemFromInventory(iPtr->GetBagSlot(), iPtr->GetSlot(), true);
}
}
// execute trade: 2. store
moveItems(myItems, hisItems);
// logging money
if (sWorld.getConfig(CONFIG_GM_LOG_TRADE))
{
if (_player->GetSession()->GetSecurity() > SEC_PLAYER && _player->tradeGold > 0)
{
sLog.outCommand(_player->GetSession()->GetAccountId(),"GM %s (Account: %u) give money (Amount: %u) to player: %s (Account: %u)",
_player->GetName(),_player->GetSession()->GetAccountId(),
_player->tradeGold,
_player->pTrader->GetName(),_player->pTrader->GetSession()->GetAccountId());
}
if (_player->pTrader->GetSession()->GetSecurity() > SEC_PLAYER && _player->pTrader->tradeGold > 0)
{
sLog.outCommand(_player->pTrader->GetSession()->GetAccountId(),"GM %s (Account: %u) give money (Amount: %u) to player: %s (Account: %u)",
_player->pTrader->GetName(),_player->pTrader->GetSession()->GetAccountId(),
_player->pTrader->tradeGold,
_player->GetName(),_player->GetSession()->GetAccountId());
}
}
// update money
_player->ModifyMoney(-int32(_player->tradeGold));
_player->ModifyMoney(_player->pTrader->tradeGold);
_player->pTrader->ModifyMoney(-int32(_player->pTrader->tradeGold));
_player->pTrader->ModifyMoney(_player->tradeGold);
_player->ClearTrade();
_player->pTrader->ClearTrade();
// desynchronized with the other saves here (SaveInventoryAndGoldToDB() not have own transaction guards)
CharacterDatabase.BeginTransaction();
_player->SaveInventoryAndGoldToDB();
_player->pTrader->SaveInventoryAndGoldToDB();
CharacterDatabase.CommitTransaction();
_player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_COMPLETE);
SendTradeStatus(TRADE_STATUS_TRADE_COMPLETE);
_player->pTrader->pTrader = NULL;
_player->pTrader = NULL;
}
else
{
_player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_ACCEPT);
}
}
void WorldSession::HandleUnacceptTradeOpcode(WorldPacket& /*recvPacket*/)
{
if (!GetPlayer()->pTrader)
return;
_player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
_player->acceptTrade = false;
}
void WorldSession::HandleBeginTradeOpcode(WorldPacket& /*recvPacket*/)
{
if (!_player->pTrader)
return;
_player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_OPEN_WINDOW);
_player->pTrader->ClearTrade();
SendTradeStatus(TRADE_STATUS_OPEN_WINDOW);
_player->ClearTrade();
}
void WorldSession::SendCancelTrade()
{
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()->pTrader)
return;
uint64 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.getConfig(CONFIG_TRADE_LEVEL_REQ))
{
SendNotification(GetTrinityString(LANG_TRADE_REQ), sWorld.getConfig(CONFIG_TRADE_LEVEL_REQ));
return;
}
recvPacket >> ID;
Player* pOther = ObjectAccessor::FindPlayer(ID);
if (!pOther)
{
SendTradeStatus(TRADE_STATUS_NO_TARGET);
return;
}
if (pOther == GetPlayer() || pOther->pTrader)
{
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.getConfig(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.getConfig(CONFIG_TRADE_LEVEL_REQ))
{
SendNotification(GetTrinityString(LANG_TRADE_OTHER_REQ), sWorld.getConfig(CONFIG_TRADE_LEVEL_REQ));
return;
}
// OK start trade
_player->pTrader = pOther;
pOther->pTrader =_player;
WorldPacket data(SMSG_TRADE_STATUS, 12);
data << (uint32) TRADE_STATUS_BEGIN_TRADE;
data << (uint64)_player->GetGUID();
_player->pTrader->GetSession()->SendPacket(&data);
}
void WorldSession::HandleSetTradeGoldOpcode(WorldPacket& recvPacket)
{
if (!_player->pTrader)
return;
uint32 gold;
recvPacket >> gold;
// gold can be incorrect, but this is checked at trade finished.
_player->tradeGold = gold;
_player->pTrader->GetSession()->SendUpdateTrade();
}
void WorldSession::HandleSetTradeItemOpcode(WorldPacket& recvPacket)
{
if (!_player->pTrader)
return;
// send update
uint8 tradeSlot;
uint8 bag;
uint8 slot;
recvPacket >> tradeSlot;
recvPacket >> bag;
recvPacket >> slot;
// 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()))
{
SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
return;
}
uint64 iGUID = item->GetGUID();
// prevent place single item into many trade slots using cheating and client bugs
for (int i = 0; i < TRADE_SLOT_COUNT; ++i)
{
if (_player->tradeItems[i] == iGUID)
{
// cheating attempt
SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
return;
}
}
_player->tradeItems[tradeSlot] = iGUID;
_player->pTrader->GetSession()->SendUpdateTrade();
}
void WorldSession::HandleClearTradeItemOpcode(WorldPacket& recvPacket)
{
if (!_player->pTrader)
return;
uint8 tradeSlot;
recvPacket >> tradeSlot;
// invalid slot number
if (tradeSlot >= TRADE_SLOT_COUNT)
return;
_player->tradeItems[tradeSlot] = 0;
_player->pTrader->GetSession()->SendUpdateTrade();
}
@@ -0,0 +1,50 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 Trinity <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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "Common.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "Opcodes.h"
#include "Log.h"
void WorldSession::HandleVoiceSessionEnableOpcode(WorldPacket & recv_data)
{
sLog.outDebug("WORLD: CMSG_VOICE_SESSION_ENABLE");
// uint8 isVoiceEnabled, uint8 isMicrophoneEnabled
recv_data.read_skip<uint8>();
recv_data.read_skip<uint8>();
recv_data.hexlike();
}
void WorldSession::HandleChannelVoiceOnOpcode(WorldPacket & recv_data)
{
sLog.outDebug("WORLD: CMSG_CHANNEL_VOICE_ON");
// Enable Voice button in channel context menu
recv_data.hexlike();
}
void WorldSession::HandleSetActiveVoiceChannel(WorldPacket & recv_data)
{
sLog.outDebug("WORLD: CMSG_SET_ACTIVE_VOICE_CHANNEL");
recv_data.read_skip<uint32>();
recv_data.read_skip<char*>();
recv_data.hexlike();
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,122 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 Trinity <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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/** \file
\ingroup u2w
*/
#include "WorldLog.h"
#include "Policies/SingletonImp.h"
#include "Config/ConfigEnv.h"
#include "Log.h"
#define CLASS_LOCK Trinity::ClassLevelLockable<WorldLog, ACE_Thread_Mutex>
INSTANTIATE_SINGLETON_2(WorldLog, CLASS_LOCK);
INSTANTIATE_CLASS_MUTEX(WorldLog, ACE_Thread_Mutex);
WorldLog::WorldLog() : i_file(NULL)
{
Initialize();
}
WorldLog::~WorldLog()
{
if (i_file != NULL)
fclose(i_file);
i_file = NULL;
}
/// Open the log file (if specified so in the configuration file)
void WorldLog::Initialize()
{
std::string logsDir = sConfig.GetStringDefault("LogsDir","");
if (!logsDir.empty())
{
if ((logsDir.at(logsDir.length()-1) != '/') && (logsDir.at(logsDir.length()-1) != '\\'))
logsDir.append("/");
}
std::string logname = sConfig.GetStringDefault("WorldLogFile", "");
if (!logname.empty())
{
i_file = fopen((logsDir+logname).c_str(), "w");
}
m_dbWorld = sConfig.GetBoolDefault("LogDB.World", false); // can be VERY heavy if enabled
}
void WorldLog::outTimestampLog(char const *fmt, ...)
{
if (LogWorld())
{
Guard guard(*this);
ASSERT(i_file);
Log::outTimestamp(i_file);
va_list args;
va_start(args, fmt);
vfprintf(i_file, fmt, args);
//fprintf(i_file, "\n");
va_end(args);
fflush(i_file);
}
if (sLog.GetLogDB() && m_dbWorld)
{
va_list ap2;
va_start(ap2, fmt);
char nnew_str[MAX_QUERY_LEN];
vsnprintf(nnew_str, MAX_QUERY_LEN, fmt, ap2);
sLog.outDB(LOG_TYPE_WORLD, nnew_str);
va_end(ap2);
}
}
void WorldLog::outLog(char const *fmt, ...)
{
if (LogWorld())
{
Guard guard(*this);
ASSERT(i_file);
va_list args;
va_start(args, fmt);
vfprintf(i_file, fmt, args);
//fprintf(i_file, "\n");
va_end(args);
fflush(i_file);
}
if (sLog.GetLogDB() && m_dbWorld)
{
va_list ap2;
va_start(ap2, fmt);
char nnew_str[MAX_QUERY_LEN];
vsnprintf(nnew_str, MAX_QUERY_LEN, fmt, ap2);
sLog.outDB(LOG_TYPE_WORLD, nnew_str);
va_end(ap2);
}
}
#define sWorldLog WorldLog::Instance()
@@ -0,0 +1,63 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 Trinity <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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/// \addtogroup u2w
/// @{
/// \file
#ifndef TRINITY_WORLDLOG_H
#define TRINITY_WORLDLOG_H
#include "Common.h"
#include "Policies/Singleton.h"
#include "Errors.h"
#include <stdarg.h>
/// %Log packets to a file
class WorldLog : public Trinity::Singleton<WorldLog, Trinity::ClassLevelLockable<WorldLog, ACE_Thread_Mutex> >
{
friend class Trinity::OperatorNew<WorldLog>;
WorldLog();
WorldLog(const WorldLog &);
WorldLog& operator=(const WorldLog &);
typedef Trinity::ClassLevelLockable<WorldLog, ACE_Thread_Mutex>::Lock Guard;
/// Close the file in destructor
~WorldLog();
public:
void Initialize();
/// Is the world logger active?
bool LogWorld(void) const { return (i_file != NULL); }
/// %Log to the file
void outLog(char const *fmt, ...);
void outTimestampLog(char const *fmt, ...);
private:
FILE *i_file;
bool m_dbWorld;
};
#define sWorldLog WorldLog::Instance()
#endif
/// @}
+959
View File
@@ -0,0 +1,959 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 Trinity <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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/** \file
\ingroup u2w
*/
#include "WorldSocket.h" // must be first to make ACE happy with ACE includes in it
#include "Common.h"
#include "Database/DatabaseEnv.h"
#include "Log.h"
#include "Opcodes.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "Player.h"
#include "Vehicle.h"
#include "ObjectMgr.h"
#include "Group.h"
#include "Guild.h"
#include "World.h"
#include "ObjectAccessor.h"
#include "BattleGroundMgr.h"
#include "OutdoorPvPMgr.h"
#include "MapManager.h"
#include "SocialMgr.h"
#include "zlib/zlib.h"
#include "ScriptMgr.h"
#include "LFGMgr.h"
/// WorldSession constructor
WorldSession::WorldSession(uint32 id, WorldSocket *sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale) :
LookingForGroup_auto_join(false), LookingForGroup_auto_add(false), m_muteTime(mute_time),
_player(NULL), m_Socket(sock),_security(sec), _accountId(id), m_expansion(expansion),
m_sessionDbcLocale(sWorld.GetAvailableDbcLocale(locale)), m_sessionDbLocaleIndex(objmgr.GetIndexForLocale(locale)),
_logoutTime(0), m_inQueue(false), m_playerLoading(false), m_playerLogout(false), m_playerRecentlyLogout(false), m_playerSave(false),
m_latency(0), m_TutorialsChanged(false), m_timeOutTime(0)
{
if (sock)
{
m_Address = sock->GetRemoteAddress ();
sock->AddReference ();
ResetTimeOutTime();
LoginDatabase.PExecute("UPDATE account SET online = 1 WHERE id = %u;", GetAccountId());
}
}
/// WorldSession destructor
WorldSession::~WorldSession()
{
///- unload player if not unloaded
if (_player)
LogoutPlayer (true);
/// - If have unclosed socket, close it
if (m_Socket)
{
m_Socket->CloseSocket ();
m_Socket->RemoveReference ();
m_Socket = NULL;
}
///- empty incoming packet queue
WorldPacket* packet;
while (_recvQueue.next(packet))
delete packet;
LoginDatabase.PExecute("UPDATE account SET online = 0 WHERE id = %u;", GetAccountId());
CharacterDatabase.PExecute("UPDATE characters SET online = 0 WHERE account = %u;", GetAccountId());
}
void WorldSession::SizeError(WorldPacket const& packet, uint32 size) const
{
sLog.outError("Client (account %u) send packet %s (%u) with size " SIZEFMTD " but expected %u (attempt crash server?), skipped",
GetAccountId(),LookupOpcodeName(packet.GetOpcode()),packet.GetOpcode(),packet.size(),size);
}
/// Get the player name
char const* WorldSession::GetPlayerName() const
{
return GetPlayer() ? GetPlayer()->GetName() : "<none>";
}
/// Send a packet to the client
void WorldSession::SendPacket(WorldPacket const* packet)
{
if (!m_Socket)
return;
#ifdef TRINITY_DEBUG
// Code for network use statistic
static uint64 sendPacketCount = 0;
static uint64 sendPacketBytes = 0;
static time_t firstTime = time(NULL);
static time_t lastTime = firstTime; // next 60 secs start time
static uint64 sendLastPacketCount = 0;
static uint64 sendLastPacketBytes = 0;
time_t cur_time = time(NULL);
if ((cur_time - lastTime) < 60)
{
sendPacketCount+=1;
sendPacketBytes+=packet->size();
sendLastPacketCount+=1;
sendLastPacketBytes+=packet->size();
}
else
{
uint64 minTime = uint64(cur_time - lastTime);
uint64 fullTime = uint64(lastTime - firstTime);
sLog.outDetail("Send all time packets count: " UI64FMTD " bytes: " UI64FMTD " avr.count/sec: %f avr.bytes/sec: %f time: %u",sendPacketCount,sendPacketBytes,float(sendPacketCount)/fullTime,float(sendPacketBytes)/fullTime,uint32(fullTime));
sLog.outDetail("Send last min packets count: " UI64FMTD " bytes: " UI64FMTD " avr.count/sec: %f avr.bytes/sec: %f",sendLastPacketCount,sendLastPacketBytes,float(sendLastPacketCount)/minTime,float(sendLastPacketBytes)/minTime);
lastTime = cur_time;
sendLastPacketCount = 1;
sendLastPacketBytes = packet->wpos(); // wpos is real written size
}
#endif // !TRINITY_DEBUG
if (m_Socket->SendPacket (*packet) == -1)
m_Socket->CloseSocket ();
}
/// Add an incoming packet to the queue
void WorldSession::QueuePacket(WorldPacket* new_packet)
{
_recvQueue.add(new_packet);
}
/// Logging helper for unexpected opcodes
void WorldSession::LogUnexpectedOpcode(WorldPacket* packet, const char *reason)
{
sLog.outError("SESSION: received unexpected opcode %s (0x%.4X) %s",
LookupOpcodeName(packet->GetOpcode()),
packet->GetOpcode(),
reason);
}
/// Logging helper for unexpected opcodes
void WorldSession::LogUnprocessedTail(WorldPacket *packet)
{
sLog.outError("SESSION: opcode %s (0x%.4X) have unprocessed tail data (read stop at %u from %u)",
LookupOpcodeName(packet->GetOpcode()),
packet->GetOpcode(),
packet->rpos(),packet->wpos());
packet->print_storage();
}
/// Update the WorldSession (triggered by World update)
bool WorldSession::Update(uint32 diff)
{
/// Update Timeout timer.
UpdateTimeOutTime(diff);
///- Before we process anything:
/// If necessary, kick the player from the character select screen
if (IsConnectionIdle())
m_Socket->CloseSocket();
///- Retrieve packets from the receive queue and call the appropriate handlers
/// not proccess packets if socket already closed
WorldPacket* packet;
while (m_Socket && !m_Socket->IsClosed() && _recvQueue.next(packet))
{
/*#if 1
sLog.outError("MOEP: %s (0x%.4X)",
LookupOpcodeName(packet->GetOpcode()),
packet->GetOpcode());
#endif*/
if (packet->GetOpcode() >= NUM_MSG_TYPES)
{
sLog.outError("SESSION: received non-existed opcode %s (0x%.4X)",
LookupOpcodeName(packet->GetOpcode()),
packet->GetOpcode());
}
else
{
OpcodeHandler& opHandle = opcodeTable[packet->GetOpcode()];
try
{
switch (opHandle.status)
{
case STATUS_LOGGEDIN:
if (!_player)
{
// skip STATUS_LOGGEDIN opcode unexpected errors if player logout sometime ago - this can be network lag delayed packets
if (!m_playerRecentlyLogout)
LogUnexpectedOpcode(packet, "the player has not logged in yet");
}
else if (_player->IsInWorld())
{
(this->*opHandle.handler)(*packet);
if (sLog.IsOutDebug() && packet->rpos() < packet->wpos())
LogUnprocessedTail(packet);
}
// lag can cause STATUS_LOGGEDIN opcodes to arrive after the player started a transfer
break;
case STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT:
if (!_player && !m_playerRecentlyLogout)
{
LogUnexpectedOpcode(packet, "the player has not logged in yet and not recently logout");
}
else
{
// not expected _player or must checked in packet hanlder
(this->*opHandle.handler)(*packet);
if (sLog.IsOutDebug() && packet->rpos() < packet->wpos())
LogUnprocessedTail(packet);
}
break;
case STATUS_TRANSFER:
if (!_player)
LogUnexpectedOpcode(packet, "the player has not logged in yet");
else if (_player->IsInWorld())
LogUnexpectedOpcode(packet, "the player is still in world");
else
{
(this->*opHandle.handler)(*packet);
if (sLog.IsOutDebug() && packet->rpos() < packet->wpos())
LogUnprocessedTail(packet);
}
break;
case STATUS_AUTHED:
// prevent cheating with skip queue wait
if (m_inQueue)
{
LogUnexpectedOpcode(packet, "the player not pass queue yet");
break;
}
// single from authed time opcodes send in to after logout time
// and before other STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT opcodes.
if (packet->GetOpcode() != CMSG_SET_ACTIVE_VOICE_CHANNEL)
m_playerRecentlyLogout = false;
(this->*opHandle.handler)(*packet);
if (sLog.IsOutDebug() && packet->rpos() < packet->wpos())
LogUnprocessedTail(packet);
break;
case STATUS_NEVER:
/*
sLog.outError("SESSION: received not allowed opcode %s (0x%.4X)",
LookupOpcodeName(packet->GetOpcode()),
packet->GetOpcode());
*/
break;
}
}
catch(ByteBufferException &)
{
sLog.outError("WorldSession::Update ByteBufferException occured while parsing a packet (opcode: %u) from client %s, accountid=%i. Skipped packet.",
packet->GetOpcode(), GetRemoteAddress().c_str(), GetAccountId());
if (sLog.IsOutDebug())
{
sLog.outDebug("Dumping error causing packet:");
packet->hexlike();
}
}
}
delete packet;
}
time_t currTime = time(NULL);
///- If necessary, log the player out
if (ShouldLogOut(currTime) && !m_playerLoading)
LogoutPlayer(true);
///- Cleanup socket pointer if need
if (m_Socket && m_Socket->IsClosed())
{
m_Socket->RemoveReference();
m_Socket = NULL;
}
if (!m_Socket)
return false; //Will remove this session from the world session map
return true;
}
/// %Log the player out
void WorldSession::LogoutPlayer(bool Save)
{
// finish pending transfers before starting the logout
while (_player && _player->IsBeingTeleportedFar())
HandleMoveWorldportAckOpcode();
m_playerLogout = true;
m_playerSave = Save;
if (_player)
{
sLFGMgr.Leave(_player);
GetPlayer()->GetSession()->SendLfgUpdateParty(LFG_UPDATETYPE_REMOVED_FROM_QUEUE);
GetPlayer()->GetSession()->SendLfgUpdatePlayer(LFG_UPDATETYPE_REMOVED_FROM_QUEUE);
GetPlayer()->GetSession()->SendLfgUpdateSearch(false);
if (uint64 lguid = GetPlayer()->GetLootGUID())
DoLootRelease(lguid);
///- If the player just died before logging out, make him appear as a ghost
//FIXME: logout must be delayed in case lost connection with client in time of combat
if (_player->GetDeathTimer())
{
_player->getHostileRefManager().deleteReferences();
_player->BuildPlayerRepop();
_player->RepopAtGraveyard();
}
else if (!_player->getAttackers().empty())
{
_player->CombatStop();
_player->getHostileRefManager().setOnlineOfflineState(false);
_player->RemoveAllAurasOnDeath();
// build set of player who attack _player or who have pet attacking of _player
std::set<Player*> aset;
for (Unit::AttackerSet::const_iterator itr = _player->getAttackers().begin(); itr != _player->getAttackers().end(); ++itr)
{
Unit* owner = (*itr)->GetOwner(); // including player controlled case
if (owner)
{
if (owner->GetTypeId() == TYPEID_PLAYER)
aset.insert(owner->ToPlayer());
}
else
if ((*itr)->GetTypeId() == TYPEID_PLAYER)
aset.insert((Player*)(*itr));
}
_player->SetPvPDeath(!aset.empty());
_player->KillPlayer();
_player->BuildPlayerRepop();
_player->RepopAtGraveyard();
// give honor to all attackers from set like group case
for (std::set<Player*>::const_iterator itr = aset.begin(); itr != aset.end(); ++itr)
(*itr)->RewardHonor(_player,aset.size());
// give bg rewards and update counters like kill by first from attackers
// this can't be called for all attackers.
if (!aset.empty())
if (BattleGround *bg = _player->GetBattleGround())
bg->HandleKillPlayer(_player,*aset.begin());
}
else if (_player->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION))
{
// this will kill character by SPELL_AURA_SPIRIT_OF_REDEMPTION
_player->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT);
//_player->SetDeathPvP(*); set at SPELL_AURA_SPIRIT_OF_REDEMPTION apply time
_player->KillPlayer();
_player->BuildPlayerRepop();
_player->RepopAtGraveyard();
}
//drop a flag if player is carrying it
if (BattleGround *bg = _player->GetBattleGround())
bg->EventPlayerLoggedOut(_player);
///- Teleport to home if the player is in an invalid instance
if (!_player->m_InstanceValid && !_player->isGameMaster())
_player->TeleportTo(_player->m_homebindMapId, _player->m_homebindX, _player->m_homebindY, _player->m_homebindZ, _player->GetOrientation());
sOutdoorPvPMgr.HandlePlayerLeaveZone(_player,_player->GetZoneId());
for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i)
{
if (BattleGroundQueueTypeId bgQueueTypeId = _player->GetBattleGroundQueueTypeId(i))
{
_player->RemoveBattleGroundQueueId(bgQueueTypeId);
sBattleGroundMgr.m_BattleGroundQueues[ bgQueueTypeId ].RemovePlayer(_player->GetGUID(), true);
}
}
// Repop at GraveYard or other player far teleport will prevent saving player because of not present map
// Teleport player immediately for correct player save
while (_player->IsBeingTeleportedFar())
HandleMoveWorldportAckOpcode();
///- If the player is in a guild, update the guild roster and broadcast a logout message to other guild members
Guild *guild = objmgr.GetGuildById(_player->GetGuildId());
if (guild)
{
guild->SetMemberStats(_player->GetGUID());
guild->UpdateLogoutTime(_player->GetGUID());
guild->BroadcastEvent(GE_SIGNED_OFF, _player->GetGUID(), 1, _player->GetName(), "", "");
}
///- Remove pet
_player->RemovePet(NULL,PET_SAVE_AS_CURRENT, true);
///- empty buyback items and save the player in the database
// some save parts only correctly work in case player present in map/player_lists (pets, etc)
if (Save)
{
uint32 eslot;
for (int j = BUYBACK_SLOT_START; j < BUYBACK_SLOT_END; ++j)
{
eslot = j - BUYBACK_SLOT_START;
_player->SetUInt64Value(PLAYER_FIELD_VENDORBUYBACK_SLOT_1 + (eslot * 2), 0);
_player->SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1 + eslot, 0);
_player->SetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1 + eslot, 0);
}
_player->SaveToDB();
}
///- Leave all channels before player delete...
_player->CleanupChannels();
///- If the player is in a group (or invited), remove him. If the group if then only 1 person, disband the group.
_player->UninviteFromGroup();
// remove player from the group if he is:
// a) in group; b) not in raid group; c) logging out normally (not being kicked or disconnected)
if (_player->GetGroup() && !_player->GetGroup()->isRaidGroup() && m_Socket)
_player->RemoveFromGroup();
///- Send update to group and reset stored max enchanting level
if (_player->GetGroup())
{
_player->GetGroup()->SendUpdate();
_player->GetGroup()->ResetMaxEnchantingLevel();
}
///- Broadcast a logout message to the player's friends
sSocialMgr.SendFriendStatus(_player, FRIEND_OFFLINE, _player->GetGUIDLow(), true);
sSocialMgr.RemovePlayerSocial (_player->GetGUIDLow ());
///- Remove the player from the world
// the player may not be in the world when logging out
// e.g if he got disconnected during a transfer to another map
// calls to GetMap in this case may cause crashes
_player->CleanupsBeforeDelete();
sLog.outChar("Account: %d (IP: %s) Logout Character:[%s] (GUID: %u)", GetAccountId(), GetRemoteAddress().c_str(), _player->GetName() ,_player->GetGUIDLow());
Map* _map = _player->GetMap();
_map->Remove(_player, true);
SetPlayer(NULL); // deleted in Remove call
///- Send the 'logout complete' packet to the client
WorldPacket data(SMSG_LOGOUT_COMPLETE, 0);
SendPacket(&data);
///- Since each account can only have one online character at any given time, ensure all characters for active account are marked as offline
//No SQL injection as AccountId is uint32
CharacterDatabase.PExecute("UPDATE characters SET online = 0 WHERE account = '%u'",
GetAccountId());
sLog.outDebug("SESSION: Sent SMSG_LOGOUT_COMPLETE Message");
}
//Hook for OnLogout Event
sScriptMgr.OnLogout(_player);
m_playerLogout = false;
m_playerSave = false;
m_playerRecentlyLogout = true;
LogoutRequest(0);
}
/// Kick a player out of the World
void WorldSession::KickPlayer()
{
if (m_Socket)
m_Socket->CloseSocket ();
}
void WorldSession::SendNotification(const char *format,...)
{
if (format)
{
va_list ap;
char szStr [1024];
szStr[0] = '\0';
va_start(ap, format);
vsnprintf(szStr, 1024, format, ap);
va_end(ap);
WorldPacket data(SMSG_NOTIFICATION, (strlen(szStr)+1));
data << szStr;
SendPacket(&data);
}
}
void WorldSession::SendNotification(int32 string_id,...)
{
char const* format = GetTrinityString(string_id);
if (format)
{
va_list ap;
char szStr [1024];
szStr[0] = '\0';
va_start(ap, string_id);
vsnprintf(szStr, 1024, format, ap);
va_end(ap);
WorldPacket data(SMSG_NOTIFICATION, (strlen(szStr)+1));
data << szStr;
SendPacket(&data);
}
}
const char * WorldSession::GetTrinityString(int32 entry) const
{
return objmgr.GetTrinityString(entry,GetSessionDbLocaleIndex());
}
void WorldSession::Handle_NULL(WorldPacket& recvPacket)
{
sLog.outError("SESSION: received unhandled opcode %s (0x%.4X)",
LookupOpcodeName(recvPacket.GetOpcode()),
recvPacket.GetOpcode());
}
void WorldSession::Handle_EarlyProccess(WorldPacket& recvPacket)
{
sLog.outError("SESSION: received opcode %s (0x%.4X) that must be processed in WorldSocket::OnRead",
LookupOpcodeName(recvPacket.GetOpcode()),
recvPacket.GetOpcode());
}
void WorldSession::Handle_ServerSide(WorldPacket& recvPacket)
{
sLog.outError("SESSION: received server-side opcode %s (0x%.4X)",
LookupOpcodeName(recvPacket.GetOpcode()),
recvPacket.GetOpcode());
}
void WorldSession::Handle_Deprecated(WorldPacket& recvPacket)
{
sLog.outError("SESSION: received deprecated opcode %s (0x%.4X)",
LookupOpcodeName(recvPacket.GetOpcode()),
recvPacket.GetOpcode());
}
void WorldSession::SendAuthWaitQue(uint32 position)
{
if (position == 0)
{
WorldPacket packet(SMSG_AUTH_RESPONSE, 1);
packet << uint8(AUTH_OK);
SendPacket(&packet);
}
else
{
WorldPacket packet(SMSG_AUTH_RESPONSE, 6);
packet << uint8(AUTH_WAIT_QUEUE);
packet << uint32(position);
packet << uint8(0); // unk
SendPacket(&packet);
}
}
void WorldSession::LoadGlobalAccountData()
{
LoadAccountData(
CharacterDatabase.PQuery("SELECT type, time, data FROM account_data WHERE account='%u'", GetAccountId()),
GLOBAL_CACHE_MASK
);
}
void WorldSession::LoadAccountData(QueryResult_AutoPtr result, uint32 mask)
{
for (uint32 i = 0; i < NUM_ACCOUNT_DATA_TYPES; ++i)
if (mask & (1 << i))
m_accountData[i] = AccountData();
if (!result)
return;
do
{
Field *fields = result->Fetch();
uint32 type = fields[0].GetUInt32();
if (type >= NUM_ACCOUNT_DATA_TYPES)
{
sLog.outError("Table `%s` have invalid account data type (%u), ignore.",
mask == GLOBAL_CACHE_MASK ? "account_data" : "character_account_data", type);
continue;
}
if ((mask & (1 << type)) == 0)
{
sLog.outError("Table `%s` have non appropriate for table account data type (%u), ignore.",
mask == GLOBAL_CACHE_MASK ? "account_data" : "character_account_data", type);
continue;
}
m_accountData[type].Time = fields[1].GetUInt32();
m_accountData[type].Data = fields[2].GetCppString();
} while (result->NextRow());
}
void WorldSession::SetAccountData(AccountDataType type, time_t time_, std::string data)
{
if ((1 << type) & GLOBAL_CACHE_MASK)
{
uint32 acc = GetAccountId();
CharacterDatabase.BeginTransaction ();
CharacterDatabase.PExecute("DELETE FROM account_data WHERE account='%u' AND type='%u'", acc, type);
CharacterDatabase.escape_string(data);
CharacterDatabase.PExecute("INSERT INTO account_data VALUES ('%u','%u','%u','%s')", acc, type, (uint32)time_, data.c_str());
CharacterDatabase.CommitTransaction ();
}
else
{
// _player can be NULL and packet received after logout but m_GUID still store correct guid
if (!m_GUIDLow)
return;
CharacterDatabase.BeginTransaction ();
CharacterDatabase.PExecute("DELETE FROM character_account_data WHERE guid='%u' AND type='%u'", m_GUIDLow, type);
CharacterDatabase.escape_string(data);
CharacterDatabase.PExecute("INSERT INTO character_account_data VALUES ('%u','%u','%u','%s')", m_GUIDLow, type, (uint32)time_, data.c_str());
CharacterDatabase.CommitTransaction ();
}
m_accountData[type].Time = time_;
m_accountData[type].Data = data;
}
void WorldSession::SendAccountDataTimes(uint32 mask)
{
WorldPacket data(SMSG_ACCOUNT_DATA_TIMES, 4+1+4+8*4); // changed in WotLK
data << uint32(time(NULL)); // unix time of something
data << uint8(1);
data << uint32(mask); // type mask
for (uint32 i = 0; i < NUM_ACCOUNT_DATA_TYPES; ++i)
if (mask & (1 << i))
data << uint32(GetAccountData(AccountDataType(i))->Time);// also unix time
SendPacket(&data);
}
void WorldSession::LoadTutorialsData()
{
for (int aX = 0 ; aX < 8 ; ++aX)
m_Tutorials[ aX ] = 0;
QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT tut0,tut1,tut2,tut3,tut4,tut5,tut6,tut7 FROM character_tutorial WHERE account = '%u'", GetAccountId());
if (result)
{
do
{
Field *fields = result->Fetch();
for (int iI = 0; iI < 8; ++iI)
m_Tutorials[iI] = fields[iI].GetUInt32();
}
while (result->NextRow());
}
m_TutorialsChanged = false;
}
void WorldSession::SendTutorialsData()
{
WorldPacket data(SMSG_TUTORIAL_FLAGS, 4*8);
for (uint32 i = 0; i < 8; ++i)
data << m_Tutorials[i];
SendPacket(&data);
}
void WorldSession::SaveTutorialsData()
{
if (!m_TutorialsChanged)
return;
uint32 Rows=0;
// it's better than rebuilding indexes multiple times
QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT count(*) AS r FROM character_tutorial WHERE account = '%u'", GetAccountId());
if (result)
Rows = result->Fetch()[0].GetUInt32();
if (Rows)
{
CharacterDatabase.PExecute("UPDATE character_tutorial SET tut0='%u', tut1='%u', tut2='%u', tut3='%u', tut4='%u', tut5='%u', tut6='%u', tut7='%u' WHERE account = '%u'",
m_Tutorials[0], m_Tutorials[1], m_Tutorials[2], m_Tutorials[3], m_Tutorials[4], m_Tutorials[5], m_Tutorials[6], m_Tutorials[7], GetAccountId());
}
else
{
CharacterDatabase.PExecute("INSERT INTO character_tutorial (account,tut0,tut1,tut2,tut3,tut4,tut5,tut6,tut7) VALUES ('%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u')", GetAccountId(), m_Tutorials[0], m_Tutorials[1], m_Tutorials[2], m_Tutorials[3], m_Tutorials[4], m_Tutorials[5], m_Tutorials[6], m_Tutorials[7]);
}
m_TutorialsChanged = false;
}
void WorldSession::ReadMovementInfo(WorldPacket &data, MovementInfo *mi)
{
data >> mi->flags;
data >> mi->unk1;
data >> mi->time;
data >> mi->x;
data >> mi->y;
data >> mi->z;
data >> mi->o;
if (mi->flags & MOVEMENTFLAG_ONTRANSPORT)
{
if (!data.readPackGUID(mi->t_guid))
return;
data >> mi->t_x;
data >> mi->t_y;
data >> mi->t_z;
data >> mi->t_o;
data >> mi->t_time;
data >> mi->t_seat;
}
if ((mi->flags & (MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_FLYING)) || (mi->unk1 & 0x20))
{
data >> mi->s_pitch;
}
data >> mi->fallTime;
if (mi->flags & MOVEMENTFLAG_JUMPING)
{
data >> mi->j_zspeed;
data >> mi->j_sinAngle;
data >> mi->j_cosAngle;
data >> mi->j_xyspeed;
}
if (mi->flags & MOVEMENTFLAG_SPLINE)
{
data >> mi->u_unk1;
}
}
void WorldSession::WriteMovementInfo(WorldPacket *data, MovementInfo *mi)
{
data->appendPackGUID(mi->guid);
*data << mi->flags;
*data << mi->unk1;
*data << mi->time;
*data << mi->x;
*data << mi->y;
*data << mi->z;
*data << mi->o;
if (mi->HasMovementFlag(MOVEMENTFLAG_ONTRANSPORT))
{
data->appendPackGUID(mi->t_guid);
*data << mi->t_x;
*data << mi->t_y;
*data << mi->t_z;
*data << mi->t_o;
*data << mi->t_time;
*data << mi->t_seat;
}
if ((mi->HasMovementFlag(MovementFlags(MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_FLYING))) || (mi->unk1 & 0x20))
{
*data << mi->s_pitch;
}
*data << mi->fallTime;
if (mi->HasMovementFlag(MOVEMENTFLAG_JUMPING))
{
*data << mi->j_zspeed;
*data << mi->j_sinAngle;
*data << mi->j_cosAngle;
*data << mi->j_xyspeed;
}
if (mi->HasMovementFlag(MOVEMENTFLAG_SPLINE))
{
*data << mi->u_unk1;
}
}
void WorldSession::ReadAddonsInfo(WorldPacket &data)
{
if (data.rpos() + 4 > data.size())
return;
uint32 size;
data >> size;
if (!size)
return;
if (size > 0xFFFFF)
{
sLog.outError("WorldSession::ReadAddonsInfo addon info too big, size %u", size);
return;
}
uLongf uSize = size;
uint32 pos = data.rpos();
ByteBuffer addonInfo;
addonInfo.resize(size);
if (uncompress(const_cast<uint8*>(addonInfo.contents()), &uSize, const_cast<uint8*>(data.contents() + pos), data.size() - pos) == Z_OK)
{
uint32 addonsCount;
addonInfo >> addonsCount; // addons count
for (uint32 i = 0; i < addonsCount; ++i)
{
std::string addonName;
uint8 enabled;
uint32 crc, unk1;
// check next addon data format correctness
if (addonInfo.rpos()+1 > addonInfo.size())
return;
addonInfo >> addonName;
addonInfo >> enabled >> crc >> unk1;
sLog.outDetail("ADDON: Name: %s, Enabled: 0x%x, CRC: 0x%x, Unknown2: 0x%x", addonName.c_str(), enabled, crc, unk1);
AddonInfo addon(addonName, enabled, crc, 2, true);
SavedAddon const* savedAddon = sAddonMgr.GetAddonInfo(addonName);
if (savedAddon)
{
bool match = true;
if (addon.CRC != savedAddon->CRC)
match = false;
if (!match)
sLog.outDetail("ADDON: %s was known, but didn't match known CRC (0x%x)!", addon.Name.c_str(), savedAddon->CRC);
else
sLog.outDetail("ADDON: %s was known, CRC is correct (0x%x)", addon.Name.c_str(), savedAddon->CRC);
}
else
{
sAddonMgr.SaveAddon(addon);
sLog.outDetail("ADDON: %s (0x%x) was not known, saving...", addon.Name.c_str(), addon.CRC);
}
// TODO: Find out when to not use CRC/pubkey, and other possible states.
m_addonsList.push_back(addon);
}
uint32 currentTime;
addonInfo >> currentTime;
sLog.outDebug("ADDON: CurrentTime: %u", currentTime);
if (addonInfo.rpos() != addonInfo.size())
sLog.outDebug("packet under-read!");
}
else
sLog.outError("Addon packet uncompress error!");
}
void WorldSession::SendAddonsInfo()
{
uint8 addonPublicKey[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
};
WorldPacket data(SMSG_ADDON_INFO, 4);
for (AddonsList::iterator itr = m_addonsList.begin(); itr != m_addonsList.end(); ++itr)
{
data << uint8(itr->State);
uint8 crcpub = itr->UsePublicKeyOrCRC;
data << uint8(crcpub);
if (crcpub)
{
uint8 usepk = (itr->CRC != STANDARD_ADDON_CRC); // If addon is Standard addon CRC
data << uint8(usepk);
if (usepk) // if CRC is wrong, add public key (client need it)
{
sLog.outDetail("ADDON: CRC (0x%x) for addon %s is wrong (does not match expected 0x%x), sending pubkey",
itr->CRC, itr->Name.c_str(), STANDARD_ADDON_CRC);
data.append(addonPublicKey, sizeof(addonPublicKey));
}
data << uint32(/*itr->CRC*/ 0); // TODO: Find out the meaning of this.
}
uint8 unk3 = 0; // 0 is sent here
data << uint8(unk3);
if (unk3)
{
// String, length 256 (null terminated)
data << uint8(0);
}
}
m_addonsList.clear();
uint32 count = 0;
data << uint32(count);
/*for (uint32 i = 0; i < count; ++i)
{
uint32
string (16 bytes)
string (16 bytes)
uint32
uint32
}*/
SendPacket(&data);
}
void WorldSession::SetPlayer(Player *plr)
{
_player = plr;
// set m_GUID that can be used while player loggined and later until m_playerRecentlyLogout not reset
if (_player)
m_GUIDLow = _player->GetGUIDLow();
}
+829
View File
@@ -0,0 +1,829 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 Trinity <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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/// \addtogroup u2w
/// @{
/// \file
#ifndef __WORLDSESSION_H
#define __WORLDSESSION_H
#include "Common.h"
#include "SharedDefines.h"
#include "AddonMgr.h"
#include "QueryResult.h"
#include "World.h"
struct ItemPrototype;
struct AuctionEntry;
struct DeclinedName;
struct MovementInfo;
class Creature;
class Item;
class Object;
class Player;
class Unit;
class GameObject;
class WorldPacket;
class WorldSocket;
class QueryResult;
class LoginQueryHolder;
class CharacterHandler;
struct AreaTableEntry;
enum AccountDataType
{
GLOBAL_CONFIG_CACHE = 0, // 0x01 g
PER_CHARACTER_CONFIG_CACHE = 1, // 0x02 p
GLOBAL_BINDINGS_CACHE = 2, // 0x04 g
PER_CHARACTER_BINDINGS_CACHE = 3, // 0x08 p
GLOBAL_MACROS_CACHE = 4, // 0x10 g
PER_CHARACTER_MACROS_CACHE = 5, // 0x20 p
PER_CHARACTER_LAYOUT_CACHE = 6, // 0x40 p
PER_CHARACTER_CHAT_CACHE = 7, // 0x80 p
};
#define NUM_ACCOUNT_DATA_TYPES 8
#define GLOBAL_CACHE_MASK 0x15
#define PER_CHARACTER_CACHE_MASK 0xEA
struct AccountData
{
AccountData() : Time(0), Data("") {}
time_t Time;
std::string Data;
};
enum PartyOperation
{
PARTY_OP_INVITE = 0,
PARTY_OP_UNINVITE = 1,
PARTY_OP_LEAVE = 2,
PARTY_OP_SWAP = 4
};
enum PartyResult
{
ERR_PARTY_RESULT_OK = 0,
ERR_BAD_PLAYER_NAME_S = 1,
ERR_TARGET_NOT_IN_GROUP_S = 2,
ERR_TARGET_NOT_IN_INSTANCE_S = 3,
ERR_GROUP_FULL = 4,
ERR_ALREADY_IN_GROUP_S = 5,
ERR_NOT_IN_GROUP = 6,
ERR_NOT_LEADER = 7,
ERR_PLAYER_WRONG_FACTION = 8,
ERR_IGNORING_YOU_S = 9,
ERR_LFG_PENDING = 12,
ERR_INVITE_RESTRICTED = 13,
ERR_GROUP_SWAP_FAILED = 14, // if (PartyOperation == PARTY_OP_SWAP) ERR_GROUP_SWAP_FAILED else ERR_INVITE_IN_COMBAT
ERR_INVITE_UNKNOWN_REALM = 15,
ERR_INVITE_NO_PARTY_SERVER = 16,
ERR_INVITE_PARTY_BUSY = 17,
ERR_PARTY_TARGET_AMBIGUOUS = 18,
ERR_PARTY_LFG_INVITE_RAID_LOCKED = 19,
ERR_PARTY_LFG_BOOT_LIMIT = 20,
ERR_PARTY_LFG_BOOT_COOLDOWN_S = 21,
ERR_PARTY_LFG_BOOT_IN_PROGRESS = 22,
ERR_PARTY_LFG_BOOT_TOO_FEW_PLAYERS = 23,
ERR_PARTY_LFG_BOOT_NOT_ELIGIBLE_S = 24,
ERR_RAID_DISALLOWED_BY_LEVEL = 25,
ERR_PARTY_LFG_BOOT_IN_COMBAT = 26,
ERR_VOTE_KICK_REASON_NEEDED = 27,
ERR_PARTY_LFG_BOOT_DUNGEON_COMPLETE = 28,
ERR_PARTY_LFG_BOOT_LOOT_ROLLS = 29,
ERR_PARTY_LFG_TELEPORT_IN_COMBAT = 30
};
enum ChatRestrictionType
{
ERR_CHAT_RESTRICTED = 0,
ERR_CHAT_THROTTLED = 1,
ERR_USER_SQUELCHED = 2,
ERR_YELL_RESTRICTED = 3
};
/// Player session in the World
class WorldSession
{
friend class CharacterHandler;
public:
WorldSession(uint32 id, WorldSocket *sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale);
~WorldSession();
bool PlayerLoading() const { return m_playerLoading; }
bool PlayerLogout() const { return m_playerLogout; }
bool PlayerLogoutWithSave() const { return m_playerLogout && m_playerSave; }
void SizeError(WorldPacket const& packet, uint32 size) const;
void ReadAddonsInfo(WorldPacket &data);
void SendAddonsInfo();
void ReadMovementInfo(WorldPacket &data, MovementInfo *mi);
void WriteMovementInfo(WorldPacket *data, MovementInfo *mi);
void SendPacket(WorldPacket const* packet);
void SendNotification(const char *format,...) ATTR_PRINTF(2,3);
void SendNotification(int32 string_id,...);
void SendPetNameInvalid(uint32 error, const std::string& name, DeclinedName *declinedName);
void SendPartyResult(PartyOperation operation, const std::string& member, PartyResult res);
void SendAreaTriggerMessage(const char* Text, ...) ATTR_PRINTF(2,3);
void SendSetPhaseShift(uint32 phaseShift);
void SendQueryTimeResponse();
AccountTypes GetSecurity() const { return _security; }
uint32 GetAccountId() const { return _accountId; }
Player* GetPlayer() const { return _player; }
char const* GetPlayerName() const;
void SetSecurity(AccountTypes security) { _security = security; }
std::string const& GetRemoteAddress() { return m_Address; }
void SetPlayer(Player *plr);
uint8 Expansion() const { return m_expansion; }
/// Session in auth.queue currently
void SetInQueue(bool state) { m_inQueue = state; }
/// Is the user engaged in a log out process?
bool isLogingOut() const { return _logoutTime || m_playerLogout; }
/// Engage the logout process for the user
void LogoutRequest(time_t requestTime)
{
_logoutTime = requestTime;
}
/// Is logout cooldown expired?
bool ShouldLogOut(time_t currTime) const
{
return (_logoutTime > 0 && currTime >= _logoutTime + 20);
}
void LogoutPlayer(bool Save);
void KickPlayer();
void QueuePacket(WorldPacket* new_packet);
bool Update(uint32 diff);
/// Handle the authentication waiting queue (to be completed)
void SendAuthWaitQue(uint32 position);
//void SendTestCreatureQueryOpcode(uint32 entry, uint64 guid, uint32 testvalue);
void SendNameQueryOpcode(Player* p);
void SendNameQueryOpcodeFromDB(uint64 guid);
static void SendNameQueryOpcodeFromDBCallBack(QueryResult_AutoPtr result, uint32 accountId);
void SendTrainerList(uint64 guid);
void SendTrainerList(uint64 guid, const std::string& strTitle);
void SendListInventory(uint64 guid);
void SendShowBank(uint64 guid);
void SendTabardVendorActivate(uint64 guid);
void SendSpiritResurrect();
void SendBindPoint(Creature* npc);
void SendAttackStop(Unit const* enemy);
void SendBattlegGroundList(uint64 guid, BattleGroundTypeId bgTypeId);
void SendTradeStatus(uint32 status);
void SendCancelTrade();
void SendStablePet(uint64 guid);
void SendPetitionQueryOpcode(uint64 petitionguid);
void SendUpdateTrade();
//pet
void SendPetNameQuery(uint64 guid, uint32 petnumber);
// Account Data
AccountData *GetAccountData(AccountDataType type) { return &m_accountData[type]; }
void SetAccountData(AccountDataType type, time_t time_, std::string data);
void SendAccountDataTimes(uint32 mask);
void LoadGlobalAccountData();
void LoadAccountData(QueryResult_AutoPtr result, uint32 mask);
void LoadTutorialsData();
void SendTutorialsData();
void SaveTutorialsData();
uint32 GetTutorialInt(uint32 intId)
{
return m_Tutorials[intId];
}
void SetTutorialInt(uint32 intId, uint32 value)
{
if (m_Tutorials[intId] != value)
{
m_Tutorials[intId] = value;
m_TutorialsChanged = true;
}
}
//used with item_page table
bool SendItemInfo(uint32 itemid, WorldPacket data);
//auction
void SendAuctionHello(uint64 guid, Creature * unit);
void SendAuctionCommandResult(uint32 auctionId, uint32 Action, uint32 ErrorCode, uint32 bidError = 0);
void SendAuctionBidderNotification(uint32 location, uint32 auctionId, uint64 bidder, uint32 bidSum, uint32 diff, uint32 item_template);
void SendAuctionOwnerNotification(AuctionEntry * auction);
void SendAuctionOutbiddedMail(AuctionEntry * auction, uint32 newPrice);
void SendAuctionCancelledToBidderMail(AuctionEntry* auction);
//Item Enchantment
void SendEnchantmentLog(uint64 Target, uint64 Caster,uint32 ItemID,uint32 SpellID);
void SendItemEnchantTimeUpdate(uint64 Playerguid, uint64 Itemguid,uint32 slot,uint32 Duration);
//Taxi
void SendTaxiStatus(uint64 guid);
void SendTaxiMenu(Creature* unit);
void SendDoFlight(uint32 mountDisplayId, uint32 path, uint32 pathNode = 0);
bool SendLearnNewTaxiNode(Creature* unit);
// Guild/Arena Team
void SendGuildCommandResult(uint32 typecmd, const std::string& str, uint32 cmdresult);
void SendArenaTeamCommandResult(uint32 team_action, const std::string& team, const std::string& player, uint32 error_id);
void SendNotInArenaTeamPacket(uint8 type);
void SendPetitionShowList(uint64 guid);
void SendSaveGuildEmblem(uint32 msg);
// Looking For Group
// TRUE values set by client sending CMSG_LFG_SET_AUTOJOIN and CMSG_LFM_CLEAR_AUTOFILL before player login
bool LookingForGroup_auto_join;
bool LookingForGroup_auto_add;
void BuildPartyMemberStatsChangedPacket(Player *player, WorldPacket *data);
void DoLootRelease(uint64 lguid);
// Account mute time
time_t m_muteTime;
// Locales
LocaleConstant GetSessionDbcLocale() const { return m_sessionDbcLocale; }
int GetSessionDbLocaleIndex() const { return m_sessionDbLocaleIndex; }
const char *GetTrinityString(int32 entry) const;
uint32 GetLatency() const { return m_latency; }
void SetLatency(uint32 latency) { m_latency = latency; }
uint32 getDialogStatus(Player *pPlayer, Object* questgiver, uint32 defstatus);
time_t m_timeOutTime;
void UpdateTimeOutTime(uint32 diff)
{
if (diff > m_timeOutTime)
m_timeOutTime = 0;
else
m_timeOutTime -= diff;
}
void ResetTimeOutTime()
{
m_timeOutTime = sWorld.getConfig(CONFIG_SOCKET_TIMEOUTTIME);
}
bool IsConnectionIdle() const
{
if (m_timeOutTime <= 0 && !m_inQueue)
return true;
return false;
}
public: // opcodes handlers
void Handle_NULL(WorldPacket& recvPacket); // not used
void Handle_EarlyProccess(WorldPacket& recvPacket);// just mark packets processed in WorldSocket::OnRead
void Handle_ServerSide(WorldPacket& recvPacket); // sever side only, can't be accepted from client
void Handle_Deprecated(WorldPacket& recvPacket); // never used anymore by client
void HandleCharEnumOpcode(WorldPacket& recvPacket);
void HandleCharDeleteOpcode(WorldPacket& recvPacket);
void HandleCharCreateOpcode(WorldPacket& recvPacket);
void HandlePlayerLoginOpcode(WorldPacket& recvPacket);
void HandleCharEnum(QueryResult_AutoPtr result);
void HandlePlayerLogin(LoginQueryHolder * holder);
// played time
void HandlePlayedTime(WorldPacket& recvPacket);
// new
void HandleMoveUnRootAck(WorldPacket& recvPacket);
void HandleMoveRootAck(WorldPacket& recvPacket);
void HandleLookingForGroup(WorldPacket& recvPacket);
// new inspect
void HandleInspectOpcode(WorldPacket& recvPacket);
// new party stats
void HandleInspectHonorStatsOpcode(WorldPacket& recvPacket);
void HandleMoveWaterWalkAck(WorldPacket& recvPacket);
void HandleFeatherFallAck(WorldPacket &recv_data);
void HandleMoveHoverAck(WorldPacket & recv_data);
void HandleMountSpecialAnimOpcode(WorldPacket &recvdata);
// character view
void HandleShowingHelmOpcode(WorldPacket& recv_data);
void HandleShowingCloakOpcode(WorldPacket& recv_data);
// repair
void HandleRepairItemOpcode(WorldPacket& recvPacket);
// Knockback
void HandleMoveKnockBackAck(WorldPacket& recvPacket);
void HandleMoveTeleportAck(WorldPacket& recvPacket);
void HandleForceSpeedChangeAck(WorldPacket & recv_data);
void HandlePingOpcode(WorldPacket& recvPacket);
void HandleAuthSessionOpcode(WorldPacket& recvPacket);
void HandleRepopRequestOpcode(WorldPacket& recvPacket);
void HandleAutostoreLootItemOpcode(WorldPacket& recvPacket);
void HandleLootMoneyOpcode(WorldPacket& recvPacket);
void HandleLootOpcode(WorldPacket& recvPacket);
void HandleLootReleaseOpcode(WorldPacket& recvPacket);
void HandleLootMasterGiveOpcode(WorldPacket& recvPacket);
void HandleWhoOpcode(WorldPacket& recvPacket);
void HandleLogoutRequestOpcode(WorldPacket& recvPacket);
void HandlePlayerLogoutOpcode(WorldPacket& recvPacket);
void HandleLogoutCancelOpcode(WorldPacket& recvPacket);
// GM Ticket opcodes
void HandleGMTicketCreateOpcode(WorldPacket& recvPacket);
void HandleGMTicketUpdateOpcode(WorldPacket& recvPacket);
void HandleGMTicketDeleteOpcode(WorldPacket& recvPacket);
void HandleGMTicketGetTicketOpcode(WorldPacket& recvPacket);
void HandleGMTicketSystemStatusOpcode(WorldPacket& recvPacket);
void SendGMTicketGetTicket(uint32 status, char const* text);
//void HandleGMSurveySubmit(WorldPacket& recvPacket);
void HandleTogglePvP(WorldPacket& recvPacket);
void HandleZoneUpdateOpcode(WorldPacket& recvPacket);
void HandleSetTargetOpcode(WorldPacket& recvPacket);
void HandleSetSelectionOpcode(WorldPacket& recvPacket);
void HandleStandStateChangeOpcode(WorldPacket& recvPacket);
void HandleEmoteOpcode(WorldPacket& recvPacket);
void HandleContactListOpcode(WorldPacket& recvPacket);
void HandleAddFriendOpcode(WorldPacket& recvPacket);
static void HandleAddFriendOpcodeCallBack(QueryResult_AutoPtr result, uint32 accountId, std::string friendNote);
void HandleDelFriendOpcode(WorldPacket& recvPacket);
void HandleAddIgnoreOpcode(WorldPacket& recvPacket);
static void HandleAddIgnoreOpcodeCallBack(QueryResult_AutoPtr result, uint32 accountId);
void HandleDelIgnoreOpcode(WorldPacket& recvPacket);
void HandleSetContactNotesOpcode(WorldPacket& recvPacket);
void HandleBugOpcode(WorldPacket& recvPacket);
void HandleSetAmmoOpcode(WorldPacket& recvPacket);
void HandleItemNameQueryOpcode(WorldPacket& recvPacket);
void HandleAreaTriggerOpcode(WorldPacket& recvPacket);
void HandleSetFactionAtWar(WorldPacket & recv_data);
void HandleSetFactionCheat(WorldPacket & recv_data);
void HandleSetWatchedFactionOpcode(WorldPacket & recv_data);
void HandleSetFactionInactiveOpcode(WorldPacket & recv_data);
void HandleUpdateAccountData(WorldPacket& recvPacket);
void HandleRequestAccountData(WorldPacket& recvPacket);
void HandleSetActionButtonOpcode(WorldPacket& recvPacket);
void HandleGameObjectUseOpcode(WorldPacket& recPacket);
void HandleMeetingStoneInfo(WorldPacket& recPacket);
void HandleGameobjectReportUse(WorldPacket& recvPacket);
void HandleNameQueryOpcode(WorldPacket& recvPacket);
void HandleQueryTimeOpcode(WorldPacket& recvPacket);
void HandleCreatureQueryOpcode(WorldPacket& recvPacket);
void HandleGameObjectQueryOpcode(WorldPacket& recvPacket);
void HandleMoveWorldportAckOpcode(WorldPacket& recvPacket);
void HandleMoveWorldportAckOpcode(); // for server-side calls
void HandleMovementOpcodes(WorldPacket& recvPacket);
void HandleSetActiveMoverOpcode(WorldPacket &recv_data);
void HandleMoveNotActiveMover(WorldPacket &recv_data);
void HandleDismissControlledVehicle(WorldPacket &recv_data);
void HandleRequestVehicleExit(WorldPacket &recv_data);
void HandleChangeSeatsOnControlledVehicle(WorldPacket &recv_data);
void HandleMoveTimeSkippedOpcode(WorldPacket &recv_data);
void HandleRequestRaidInfoOpcode(WorldPacket & recv_data);
void HandleBattlefieldStatusOpcode(WorldPacket &recv_data);
void HandleBattleMasterHelloOpcode(WorldPacket &recv_data);
void HandleGroupInviteOpcode(WorldPacket& recvPacket);
//void HandleGroupCancelOpcode(WorldPacket& recvPacket);
void HandleGroupAcceptOpcode(WorldPacket& recvPacket);
void HandleGroupDeclineOpcode(WorldPacket& recvPacket);
void HandleGroupUninviteOpcode(WorldPacket& recvPacket);
void HandleGroupUninviteGuidOpcode(WorldPacket& recvPacket);
void HandleGroupSetLeaderOpcode(WorldPacket& recvPacket);
void HandleGroupDisbandOpcode(WorldPacket& recvPacket);
void HandleOptOutOfLootOpcode(WorldPacket &recv_data);
void HandleLootMethodOpcode(WorldPacket& recvPacket);
void HandleLootRoll(WorldPacket &recv_data);
void HandleRequestPartyMemberStatsOpcode(WorldPacket &recv_data);
void HandleRaidTargetUpdateOpcode(WorldPacket & recv_data);
void HandleRaidReadyCheckOpcode(WorldPacket & recv_data);
void HandleRaidReadyCheckFinishedOpcode(WorldPacket & recv_data);
void HandleGroupRaidConvertOpcode(WorldPacket & recv_data);
void HandleGroupChangeSubGroupOpcode(WorldPacket & recv_data);
void HandleGroupAssistantLeaderOpcode(WorldPacket & recv_data);
void HandlePartyAssignmentOpcode(WorldPacket & recv_data);
void HandlePetitionBuyOpcode(WorldPacket& recv_data);
void HandlePetitionShowSignOpcode(WorldPacket& recv_data);
void HandlePetitionQueryOpcode(WorldPacket& recv_data);
void HandlePetitionRenameOpcode(WorldPacket& recv_data);
void HandlePetitionSignOpcode(WorldPacket& recv_data);
void HandlePetitionDeclineOpcode(WorldPacket& recv_data);
void HandleOfferPetitionOpcode(WorldPacket& recv_data);
void HandleTurnInPetitionOpcode(WorldPacket& recv_data);
void HandleGuildQueryOpcode(WorldPacket& recvPacket);
void HandleGuildCreateOpcode(WorldPacket& recvPacket);
void HandleGuildInviteOpcode(WorldPacket& recvPacket);
void HandleGuildRemoveOpcode(WorldPacket& recvPacket);
void HandleGuildAcceptOpcode(WorldPacket& recvPacket);
void HandleGuildDeclineOpcode(WorldPacket& recvPacket);
void HandleGuildInfoOpcode(WorldPacket& recvPacket);
void HandleGuildEventLogQueryOpcode(WorldPacket& recvPacket);
void HandleGuildRosterOpcode(WorldPacket& recvPacket);
void HandleGuildPromoteOpcode(WorldPacket& recvPacket);
void HandleGuildDemoteOpcode(WorldPacket& recvPacket);
void HandleGuildLeaveOpcode(WorldPacket& recvPacket);
void HandleGuildDisbandOpcode(WorldPacket& recvPacket);
void HandleGuildLeaderOpcode(WorldPacket& recvPacket);
void HandleGuildMOTDOpcode(WorldPacket& recvPacket);
void HandleGuildSetPublicNoteOpcode(WorldPacket& recvPacket);
void HandleGuildSetOfficerNoteOpcode(WorldPacket& recvPacket);
void HandleGuildRankOpcode(WorldPacket& recvPacket);
void HandleGuildAddRankOpcode(WorldPacket& recvPacket);
void HandleGuildDelRankOpcode(WorldPacket& recvPacket);
void HandleGuildChangeInfoTextOpcode(WorldPacket& recvPacket);
void HandleSaveGuildEmblemOpcode(WorldPacket& recvPacket);
void HandleTaxiNodeStatusQueryOpcode(WorldPacket& recvPacket);
void HandleTaxiQueryAvailableNodes(WorldPacket& recvPacket);
void HandleActivateTaxiOpcode(WorldPacket& recvPacket);
void HandleActivateTaxiExpressOpcode(WorldPacket& recvPacket);
void HandleMoveSplineDoneOpcode(WorldPacket& recvPacket);
void HandleTabardVendorActivateOpcode(WorldPacket& recvPacket);
void HandleBankerActivateOpcode(WorldPacket& recvPacket);
void HandleBuyBankSlotOpcode(WorldPacket& recvPacket);
void HandleTrainerListOpcode(WorldPacket& recvPacket);
void HandleTrainerBuySpellOpcode(WorldPacket& recvPacket);
void HandlePetitionShowListOpcode(WorldPacket& recvPacket);
void HandleGossipHelloOpcode(WorldPacket& recvPacket);
void HandleGossipSelectOptionOpcode(WorldPacket& recvPacket);
void HandleSpiritHealerActivateOpcode(WorldPacket& recvPacket);
void HandleNpcTextQueryOpcode(WorldPacket& recvPacket);
void HandleBinderActivateOpcode(WorldPacket& recvPacket);
void HandleListStabledPetsOpcode(WorldPacket& recvPacket);
void HandleStablePet(WorldPacket& recvPacket);
void HandleUnstablePet(WorldPacket& recvPacket);
void HandleBuyStableSlot(WorldPacket& recvPacket);
void HandleStableRevivePet(WorldPacket& recvPacket);
void HandleStableSwapPet(WorldPacket& recvPacket);
void HandleDuelAcceptedOpcode(WorldPacket& recvPacket);
void HandleDuelCancelledOpcode(WorldPacket& recvPacket);
void HandleAcceptTradeOpcode(WorldPacket& recvPacket);
void HandleBeginTradeOpcode(WorldPacket& recvPacket);
void HandleBusyTradeOpcode(WorldPacket& recvPacket);
void HandleCancelTradeOpcode(WorldPacket& recvPacket);
void HandleClearTradeItemOpcode(WorldPacket& recvPacket);
void HandleIgnoreTradeOpcode(WorldPacket& recvPacket);
void HandleInitiateTradeOpcode(WorldPacket& recvPacket);
void HandleSetTradeGoldOpcode(WorldPacket& recvPacket);
void HandleSetTradeItemOpcode(WorldPacket& recvPacket);
void HandleUnacceptTradeOpcode(WorldPacket& recvPacket);
void HandleAuctionHelloOpcode(WorldPacket& recvPacket);
void HandleAuctionListItems(WorldPacket & recv_data);
void HandleAuctionListBidderItems(WorldPacket & recv_data);
void HandleAuctionSellItem(WorldPacket & recv_data);
void HandleAuctionRemoveItem(WorldPacket & recv_data);
void HandleAuctionListOwnerItems(WorldPacket & recv_data);
void HandleAuctionPlaceBid(WorldPacket & recv_data);
void HandleAuctionListPendingSales(WorldPacket & recv_data);
void HandleGetMailList(WorldPacket & recv_data);
void HandleSendMail(WorldPacket & recv_data);
void HandleMailTakeMoney(WorldPacket & recv_data);
void HandleMailTakeItem(WorldPacket & recv_data);
void HandleMailMarkAsRead(WorldPacket & recv_data);
void HandleMailReturnToSender(WorldPacket & recv_data);
void HandleMailDelete(WorldPacket & recv_data);
void HandleItemTextQuery(WorldPacket & recv_data);
void HandleMailCreateTextItem(WorldPacket & recv_data);
void HandleQueryNextMailTime(WorldPacket & recv_data);
void HandleCancelChanneling(WorldPacket & recv_data);
void SendItemPageInfo(ItemPrototype *itemProto);
void HandleSplitItemOpcode(WorldPacket& recvPacket);
void HandleSwapInvItemOpcode(WorldPacket& recvPacket);
void HandleDestroyItemOpcode(WorldPacket& recvPacket);
void HandleAutoEquipItemOpcode(WorldPacket& recvPacket);
void HandleItemQuerySingleOpcode(WorldPacket& recvPacket);
void HandleSellItemOpcode(WorldPacket& recvPacket);
void HandleBuyItemInSlotOpcode(WorldPacket& recvPacket);
void HandleBuyItemOpcode(WorldPacket& recvPacket);
void HandleListInventoryOpcode(WorldPacket& recvPacket);
void HandleAutoStoreBagItemOpcode(WorldPacket& recvPacket);
void HandleReadItem(WorldPacket& recvPacket);
void HandleAutoEquipItemSlotOpcode(WorldPacket & recvPacket);
void HandleSwapItem(WorldPacket & recvPacket);
void HandleBuybackItem(WorldPacket & recvPacket);
void HandleAutoBankItemOpcode(WorldPacket& recvPacket);
void HandleAutoStoreBankItemOpcode(WorldPacket& recvPacket);
void HandleWrapItemOpcode(WorldPacket& recvPacket);
void HandleAttackSwingOpcode(WorldPacket& recvPacket);
void HandleAttackStopOpcode(WorldPacket& recvPacket);
void HandleSetSheathedOpcode(WorldPacket& recvPacket);
void HandleUseItemOpcode(WorldPacket& recvPacket);
void HandleOpenItemOpcode(WorldPacket& recvPacket);
void HandleCastSpellOpcode(WorldPacket& recvPacket);
void HandleCancelCastOpcode(WorldPacket& recvPacket);
void HandleCancelAuraOpcode(WorldPacket& recvPacket);
void HandleCancelGrowthAuraOpcode(WorldPacket& recvPacket);
void HandleCancelAutoRepeatSpellOpcode(WorldPacket& recvPacket);
void HandleLearnTalentOpcode(WorldPacket& recvPacket);
void HandleLearnPreviewTalents(WorldPacket& recvPacket);
void HandleTalentWipeConfirmOpcode(WorldPacket& recvPacket);
void HandleUnlearnSkillOpcode(WorldPacket& recvPacket);
void HandleQuestgiverStatusQueryOpcode(WorldPacket& recvPacket);
void HandleQuestgiverStatusMultipleQuery(WorldPacket& recvPacket);
void HandleQuestgiverHelloOpcode(WorldPacket& recvPacket);
void HandleQuestgiverAcceptQuestOpcode(WorldPacket& recvPacket);
void HandleQuestgiverQueryQuestOpcode(WorldPacket& recvPacket);
void HandleQuestgiverChooseRewardOpcode(WorldPacket& recvPacket);
void HandleQuestgiverRequestRewardOpcode(WorldPacket& recvPacket);
void HandleQuestQueryOpcode(WorldPacket& recvPacket);
void HandleQuestgiverCancel(WorldPacket& recv_data);
void HandleQuestLogSwapQuest(WorldPacket& recv_data);
void HandleQuestLogRemoveQuest(WorldPacket& recv_data);
void HandleQuestConfirmAccept(WorldPacket& recv_data);
void HandleQuestgiverCompleteQuest(WorldPacket& recv_data);
void HandleQuestgiverQuestAutoLaunch(WorldPacket& recvPacket);
void HandlePushQuestToParty(WorldPacket& recvPacket);
void HandleQuestPushResult(WorldPacket& recvPacket);
bool processChatmessageFurtherAfterSecurityChecks(std::string&, uint32);
void HandleMessagechatOpcode(WorldPacket& recvPacket);
void SendPlayerNotFoundNotice(std::string name);
void SendPlayerAmbiguousNotice(std::string name);
void SendWrongFactionNotice();
void SendChatRestrictedNotice(ChatRestrictionType restriction);
void HandleTextEmoteOpcode(WorldPacket& recvPacket);
void HandleChatIgnoredOpcode(WorldPacket& recvPacket);
void HandleReclaimCorpseOpcode(WorldPacket& recvPacket);
void HandleCorpseQueryOpcode(WorldPacket& recvPacket);
void HandleCorpseMapPositionQuery(WorldPacket& recvPacket);
void HandleResurrectResponseOpcode(WorldPacket& recvPacket);
void HandleSummonResponseOpcode(WorldPacket& recv_data);
void HandleJoinChannel(WorldPacket& recvPacket);
void HandleLeaveChannel(WorldPacket& recvPacket);
void HandleChannelList(WorldPacket& recvPacket);
void HandleChannelPassword(WorldPacket& recvPacket);
void HandleChannelSetOwner(WorldPacket& recvPacket);
void HandleChannelOwner(WorldPacket& recvPacket);
void HandleChannelModerator(WorldPacket& recvPacket);
void HandleChannelUnmoderator(WorldPacket& recvPacket);
void HandleChannelMute(WorldPacket& recvPacket);
void HandleChannelUnmute(WorldPacket& recvPacket);
void HandleChannelInvite(WorldPacket& recvPacket);
void HandleChannelKick(WorldPacket& recvPacket);
void HandleChannelBan(WorldPacket& recvPacket);
void HandleChannelUnban(WorldPacket& recvPacket);
void HandleChannelAnnouncements(WorldPacket& recvPacket);
void HandleChannelModerate(WorldPacket& recvPacket);
void HandleChannelDeclineInvite(WorldPacket& recvPacket);
void HandleChannelDisplayListQuery(WorldPacket& recvPacket);
void HandleGetChannelMemberCount(WorldPacket& recvPacket);
void HandleSetChannelWatch(WorldPacket& recvPacket);
void HandleCompleteCinematic(WorldPacket& recvPacket);
void HandleNextCinematicCamera(WorldPacket& recvPacket);
void HandlePageQuerySkippedOpcode(WorldPacket& recvPacket);
void HandlePageTextQueryOpcode(WorldPacket& recvPacket);
void HandleTutorialFlag (WorldPacket & recv_data);
void HandleTutorialClear(WorldPacket & recv_data);
void HandleTutorialReset(WorldPacket & recv_data);
//Pet
void HandlePetAction(WorldPacket & recv_data);
void HandlePetActionHelper(Unit *pet, uint64 guid1, uint16 spellid, uint16 flag, uint64 guid2);
void HandlePetNameQuery(WorldPacket & recv_data);
void HandlePetSetAction(WorldPacket & recv_data);
void HandlePetAbandon(WorldPacket & recv_data);
void HandlePetRename(WorldPacket & recv_data);
void HandlePetCancelAuraOpcode(WorldPacket& recvPacket);
void HandlePetUnlearnOpcode(WorldPacket& recvPacket);
void HandlePetSpellAutocastOpcode(WorldPacket& recvPacket);
void HandlePetCastSpellOpcode(WorldPacket& recvPacket);
void HandlePetLearnTalent(WorldPacket& recvPacket);
void HandleLearnPreviewTalentsPet(WorldPacket& recvPacket);
void HandleSetActionBarToggles(WorldPacket& recv_data);
void HandleCharRenameOpcode(WorldPacket& recv_data);
static void HandleChangePlayerNameOpcodeCallBack(QueryResult_AutoPtr result, uint32 accountId, std::string newname);
void HandleSetPlayerDeclinedNames(WorldPacket& recv_data);
void HandleTotemDestroyed(WorldPacket& recv_data);
void HandleDismissCritter(WorldPacket& recv_data);
//BattleGround
void HandleBattlemasterHelloOpcode(WorldPacket &recv_data);
void HandleBattlemasterJoinOpcode(WorldPacket &recv_data);
void HandleBattleGroundPlayerPositionsOpcode(WorldPacket& recv_data);
void HandlePVPLogDataOpcode(WorldPacket &recv_data);
void HandleBattleFieldPortOpcode(WorldPacket &recv_data);
void HandleBattlefieldListOpcode(WorldPacket &recv_data);
void HandleLeaveBattlefieldOpcode(WorldPacket &recv_data);
void HandleBattlemasterJoinArena(WorldPacket &recv_data);
void HandleReportPvPAFK(WorldPacket &recv_data);
void HandleWardenDataOpcode(WorldPacket& recv_data);
void HandleWorldTeleportOpcode(WorldPacket& recv_data);
void HandleMinimapPingOpcode(WorldPacket& recv_data);
void HandleRandomRollOpcode(WorldPacket& recv_data);
void HandleFarSightOpcode(WorldPacket& recv_data);
void HandleSetDungeonDifficultyOpcode(WorldPacket& recv_data);
void HandleSetRaidDifficultyOpcode(WorldPacket& recv_data);
void HandleMoveSetCanFlyAckOpcode(WorldPacket& recv_data);
void HandleSetTitleOpcode(WorldPacket& recv_data);
void HandleRealmSplitOpcode(WorldPacket& recv_data);
void HandleTimeSyncResp(WorldPacket& recv_data);
void HandleWhoisOpcode(WorldPacket& recv_data);
void HandleResetInstancesOpcode(WorldPacket& recv_data);
// Looking for Dungeon/Raid
void HandleSetLfgCommentOpcode(WorldPacket & recv_data);
void HandleLfgPlayerLockInfoRequestOpcode(WorldPacket& recv_data);
void HandleLfgPartyLockInfoRequestOpcode(WorldPacket& recv_data);
void HandleLfgJoinOpcode(WorldPacket &recv_data);
void HandleLfgLeaveOpcode(WorldPacket & /*recv_data*/);
void HandleLfgSetRolesOpcode(WorldPacket &recv_data);
void SendLfgUpdatePlayer(uint8 updateType);
void SendLfgUpdateParty(uint8 updateType);
void SendLfgRoleChosen(uint64 guid, uint8 roles);
void SendLfgUpdateSearch(bool update);
void SendLfgJoinResult(uint8 checkResult, uint8 checkValue);
void SendLfgQueueStatus(uint32 dungeon, int32 waitTime, int32 avgWaitTime, int32 waitTimeTanks, int32 waitTimeHealer, int32 waitTimeDps, uint32 queuedTime, uint8 tanks, uint8 healers, uint8 dps);
// Arena Team
void HandleInspectArenaTeamsOpcode(WorldPacket& recv_data);
void HandleArenaTeamQueryOpcode(WorldPacket& recv_data);
void HandleArenaTeamRosterOpcode(WorldPacket& recv_data);
void HandleArenaTeamInviteOpcode(WorldPacket& recv_data);
void HandleArenaTeamAcceptOpcode(WorldPacket& recv_data);
void HandleArenaTeamDeclineOpcode(WorldPacket& recv_data);
void HandleArenaTeamLeaveOpcode(WorldPacket& recv_data);
void HandleArenaTeamRemoveOpcode(WorldPacket& recv_data);
void HandleArenaTeamDisbandOpcode(WorldPacket& recv_data);
void HandleArenaTeamLeaderOpcode(WorldPacket& recv_data);
void HandleAreaSpiritHealerQueryOpcode(WorldPacket& recv_data);
void HandleAreaSpiritHealerQueueOpcode(WorldPacket& recv_data);
void HandleCancelMountAuraOpcode(WorldPacket& recv_data);
void HandleSelfResOpcode(WorldPacket& recv_data);
void HandleComplainOpcode(WorldPacket& recv_data);
void HandleRequestPetInfoOpcode(WorldPacket& recv_data);
// Socket gem
void HandleSocketOpcode(WorldPacket& recv_data);
void HandleCancelTempEnchantmentOpcode(WorldPacket& recv_data);
void HandleItemRefundInfoRequest(WorldPacket& recv_data);
void HandleItemRefund(WorldPacket& recv_data);
void HandleChannelVoiceOnOpcode(WorldPacket & recv_data);
void HandleVoiceSessionEnableOpcode(WorldPacket& recv_data);
void HandleSetActiveVoiceChannel(WorldPacket& recv_data);
void HandleSetTaxiBenchmarkOpcode(WorldPacket& recv_data);
// Guild Bank
void HandleGuildPermissions(WorldPacket& recv_data);
void HandleGuildBankMoneyWithdrawn(WorldPacket& recv_data);
void HandleGuildBankerActivate(WorldPacket& recv_data);
void HandleGuildBankQueryTab(WorldPacket& recv_data);
void HandleGuildBankLogQuery(WorldPacket& recv_data);
void HandleGuildBankDepositMoney(WorldPacket& recv_data);
void HandleGuildBankWithdrawMoney(WorldPacket& recv_data);
void HandleGuildBankSwapItems(WorldPacket& recv_data);
void HandleGuildBankUpdateTab(WorldPacket& recv_data);
void HandleGuildBankBuyTab(WorldPacket& recv_data);
void HandleQueryGuildBankTabText(WorldPacket& recv_data);
void HandleSetGuildBankTabText(WorldPacket& recv_data);
// Calendar
void HandleCalendarGetCalendar(WorldPacket& recv_data);
void HandleCalendarGetEvent(WorldPacket& recv_data);
void HandleCalendarGuildFilter(WorldPacket& recv_data);
void HandleCalendarArenaTeam(WorldPacket& recv_data);
void HandleCalendarAddEvent(WorldPacket& recv_data);
void HandleCalendarUpdateEvent(WorldPacket& recv_data);
void HandleCalendarRemoveEvent(WorldPacket& recv_data);
void HandleCalendarCopyEvent(WorldPacket& recv_data);
void HandleCalendarEventInvite(WorldPacket& recv_data);
void HandleCalendarEventRsvp(WorldPacket& recv_data);
void HandleCalendarEventRemoveInvite(WorldPacket& recv_data);
void HandleCalendarEventStatus(WorldPacket& recv_data);
void HandleCalendarEventModeratorStatus(WorldPacket& recv_data);
void HandleCalendarComplain(WorldPacket& recv_data);
void HandleCalendarGetNumPending(WorldPacket& recv_data);
void HandleSpellClick(WorldPacket& recv_data);
void HandleMirrrorImageDataRequest(WorldPacket & recv_data);
void HandleAlterAppearance(WorldPacket& recv_data);
void HandleRemoveGlyph(WorldPacket& recv_data);
void HandleCharCustomize(WorldPacket& recv_data);
void HandleQueryInspectAchievements(WorldPacket& recv_data);
void HandleEquipmentSetSave(WorldPacket& recv_data);
void HandleEquipmentSetDelete(WorldPacket& recv_data);
void HandleEquipmentSetUse(WorldPacket& recv_data);
void HandleWorldStateUITimerUpdate(WorldPacket& recv_data);
void HandleReadyForAccountDataTimes(WorldPacket& recv_data);
void HandleQueryQuestsCompleted(WorldPacket& recv_data);
void HandleQuestPOIQuery(WorldPacket& recv_data);
void HandleOnPVPKill(Player *killed);
bool HandleOnPlayerChat(const char *text);
uint32 HandleOnGetXP(uint32 amount);
int32 HandleOnGetMoney(int32 amount);
void HandleOnAreaChange(AreaTableEntry const *pArea);
bool HandleOnItemClick(Item *pItem);
bool HandleOnItemOpen(Item *pItem);
bool HandleOnGoClick(GameObject *pGameObject);
void HandleOnCreatureKill(Creature *pCreature);
void HandleEjectPasenger(WorldPacket &data);
void HandleEnterPlayerVehicle(WorldPacket &data);
private:
// private trade methods
void moveItems(Item* myItems[], Item* hisItems[]);
// logging helper
void LogUnexpectedOpcode(WorldPacket *packet, const char * reason);
void LogUnprocessedTail(WorldPacket *packet);
uint32 m_GUIDLow; // set loggined or recently logout player (while m_playerRecentlyLogout set)
Player *_player;
WorldSocket *m_Socket;
std::string m_Address;
AccountTypes _security;
uint32 _accountId;
uint8 m_expansion;
time_t _logoutTime;
bool m_inQueue; // session wait in auth.queue
bool m_playerLoading; // code processed in LoginPlayer
bool m_playerLogout; // code processed in LogoutPlayer
bool m_playerRecentlyLogout;
bool m_playerSave;
LocaleConstant m_sessionDbcLocale;
int m_sessionDbLocaleIndex;
uint32 m_latency;
AccountData m_accountData[NUM_ACCOUNT_DATA_TYPES];
uint32 m_Tutorials[8];
bool m_TutorialsChanged;
AddonsList m_addonsList;
ACE_Based::LockedQueue<WorldPacket*, ACE_Thread_Mutex> _recvQueue;
};
#endif
/// @}
File diff suppressed because it is too large Load Diff
+223
View File
@@ -0,0 +1,223 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 Trinity <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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/** \addtogroup u2w User to World Communication
* @{
* \file WorldSocket.h
* \author Derex <derex101@gmail.com>
*/
#ifndef _WORLDSOCKET_H
#define _WORLDSOCKET_H
#include <ace/Basic_Types.h>
#include <ace/Synch_Traits.h>
#include <ace/Svc_Handler.h>
#include <ace/SOCK_Stream.h>
#include <ace/SOCK_Acceptor.h>
#include <ace/Acceptor.h>
#include <ace/Thread_Mutex.h>
#include <ace/Guard_T.h>
#include <ace/Unbounded_Queue.h>
#include <ace/Message_Block.h>
#if !defined (ACE_LACKS_PRAGMA_ONCE)
#pragma once
#endif /* ACE_LACKS_PRAGMA_ONCE */
#include "Common.h"
#include "Auth/AuthCrypt.h"
class ACE_Message_Block;
class WorldPacket;
class WorldSession;
/// Handler that can communicate over stream sockets.
typedef ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> WorldHandler;
/**
* WorldSocket.
*
* This class is responsible for the communication with
* remote clients.
* Most methods return -1 on failure.
* The class uses reference counting.
*
* For output the class uses one buffer (64K usually) and
* a queue where it stores packet if there is no place on
* the queue. The reason this is done, is because the server
* does really a lot of small-size writes to it, and it doesn't
* scale well to allocate memory for every. When something is
* written to the output buffer the socket is not immediately
* activated for output (again for the same reason), there
* is 10ms celling (thats why there is Update() method).
* This concept is similar to TCP_CORK, but TCP_CORK
* uses 200ms celling. As result overhead generated by
* sending packets from "producer" threads is minimal,
* and doing a lot of writes with small size is tolerated.
*
* The calls to Update() method are managed by WorldSocketMgr
* and ReactorRunnable.
*
* For input ,the class uses one 1024 bytes buffer on stack
* to which it does recv() calls. And then received data is
* distributed where its needed. 1024 matches pretty well the
* traffic generated by client for now.
*
* The input/output do speculative reads/writes (AKA it tryes
* to read all data available in the kernel buffer or tryes to
* write everything available in userspace buffer),
* which is ok for using with Level and Edge Triggered IO
* notification.
*
*/
class WorldSocket : protected WorldHandler
{
public:
/// Declare some friends
friend class ACE_Acceptor< WorldSocket, ACE_SOCK_ACCEPTOR >;
friend class WorldSocketMgr;
friend class ReactorRunnable;
/// Declare the acceptor for this class
typedef ACE_Acceptor< WorldSocket, ACE_SOCK_ACCEPTOR > Acceptor;
/// Mutex type used for various synchronizations.
typedef ACE_Thread_Mutex LockType;
typedef ACE_Guard<LockType> GuardType;
/// Check if socket is closed.
bool IsClosed (void) const;
/// Close the socket.
void CloseSocket (void);
/// Get address of connected peer.
const std::string& GetRemoteAddress (void) const;
/// Send A packet on the socket, this function is reentrant.
/// @param pct packet to send
/// @return -1 of failure
int SendPacket (const WorldPacket& pct);
/// Add reference to this object.
long AddReference (void);
/// Remove reference to this object.
long RemoveReference (void);
protected:
/// things called by ACE framework.
WorldSocket (void);
virtual ~WorldSocket (void);
/// Called on open ,the void* is the acceptor.
virtual int open (void *);
/// Called on failures inside of the acceptor, don't call from your code.
virtual int close (int);
/// Called when we can read from the socket.
virtual int handle_input (ACE_HANDLE = ACE_INVALID_HANDLE);
/// Called when the socket can write.
virtual int handle_output (ACE_HANDLE = ACE_INVALID_HANDLE);
/// Called when connection is closed or error happens.
virtual int handle_close (ACE_HANDLE = ACE_INVALID_HANDLE,
ACE_Reactor_Mask = ACE_Event_Handler::ALL_EVENTS_MASK);
/// Called by WorldSocketMgr/ReactorRunnable.
int Update (void);
private:
/// Helper functions for processing incoming data.
int handle_input_header (void);
int handle_input_payload (void);
int handle_input_missing_data (void);
/// Help functions to mark/unmark the socket for output.
/// @param g the guard is for m_OutBufferLock, the function will release it
int cancel_wakeup_output (GuardType& g);
int schedule_wakeup_output (GuardType& g);
/// Drain the queue if its not empty.
int handle_output_queue (GuardType& g);
/// process one incoming packet.
/// @param new_pct received packet ,note that you need to delete it.
int ProcessIncoming (WorldPacket* new_pct);
/// Called by ProcessIncoming() on CMSG_AUTH_SESSION.
int HandleAuthSession (WorldPacket& recvPacket);
/// Called by ProcessIncoming() on CMSG_PING.
int HandlePing (WorldPacket& recvPacket);
private:
/// Time in which the last ping was received
ACE_Time_Value m_LastPingTime;
/// Keep track of over-speed pings ,to prevent ping flood.
uint32 m_OverSpeedPings;
/// Address of the remote peer
std::string m_Address;
/// Class used for managing encryption of the headers
AuthCrypt m_Crypt;
/// Mutex lock to protect m_Session
LockType m_SessionLock;
/// Session to which received packets are routed
WorldSession* m_Session;
/// here are stored the fragments of the received data
WorldPacket* m_RecvWPct;
/// This block actually refers to m_RecvWPct contents,
/// which allows easy and safe writing to it.
/// It wont free memory when its deleted. m_RecvWPct takes care of freeing.
ACE_Message_Block m_RecvPct;
/// Fragment of the received header.
ACE_Message_Block m_Header;
/// Mutex for protecting output related data.
LockType m_OutBufferLock;
/// Buffer used for writing output.
ACE_Message_Block *m_OutBuffer;
/// Size of the m_OutBuffer.
size_t m_OutBufferSize;
/// True if the socket is registered with the reactor for output
bool m_OutActive;
uint32 m_Seed;
};
#endif /* _WORLDSOCKET_H */
/// @}
+369
View File
@@ -0,0 +1,369 @@
/*
* Copyright (C) 2005-2008,2007 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 Trinity <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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/** \file WorldSocketMgr.cpp
* \ingroup u2w
* \author Derex <derex101@gmail.com>
*/
#include "WorldSocketMgr.h"
#include <ace/ACE.h>
#include <ace/Log_Msg.h>
#include <ace/Reactor.h>
#include <ace/Reactor_Impl.h>
#include <ace/TP_Reactor.h>
#include <ace/Dev_Poll_Reactor.h>
#include <ace/Guard_T.h>
#include <ace/Atomic_Op.h>
#include <ace/os_include/arpa/os_inet.h>
#include <ace/os_include/netinet/os_tcp.h>
#include <ace/os_include/sys/os_types.h>
#include <ace/os_include/sys/os_socket.h>
#include <set>
#include "Log.h"
#include "Common.h"
#include "Config/ConfigEnv.h"
#include "Database/DatabaseEnv.h"
#include "WorldSocket.h"
/**
* This is a helper class to WorldSocketMgr ,that manages
* network threads, and assigning connections from acceptor thread
* to other network threads
*/
class ReactorRunnable : protected ACE_Task_Base
{
public:
ReactorRunnable() :
m_ThreadId(-1),
m_Connections(0),
m_Reactor(0)
{
ACE_Reactor_Impl* imp = 0;
#if defined (ACE_HAS_EVENT_POLL) || defined (ACE_HAS_DEV_POLL)
imp = new ACE_Dev_Poll_Reactor();
imp->max_notify_iterations (128);
imp->restart (1);
#else
imp = new ACE_TP_Reactor();
imp->max_notify_iterations (128);
#endif
m_Reactor = new ACE_Reactor (imp, 1);
}
virtual ~ReactorRunnable()
{
Stop();
Wait();
if (m_Reactor)
delete m_Reactor;
}
void Stop()
{
m_Reactor->end_reactor_event_loop();
}
int Start()
{
if (m_ThreadId != -1)
return -1;
return (m_ThreadId = activate());
}
void Wait() { ACE_Task_Base::wait(); }
long Connections()
{
return static_cast<long> (m_Connections.value());
}
int AddSocket (WorldSocket* sock)
{
ACE_GUARD_RETURN (ACE_Thread_Mutex, Guard, m_NewSockets_Lock, -1);
++m_Connections;
sock->AddReference();
sock->reactor (m_Reactor);
m_NewSockets.insert (sock);
return 0;
}
ACE_Reactor* GetReactor()
{
return m_Reactor;
}
protected:
void AddNewSockets()
{
ACE_GUARD (ACE_Thread_Mutex, Guard, m_NewSockets_Lock);
if (m_NewSockets.empty())
return;
for (SocketSet::const_iterator i = m_NewSockets.begin(); i != m_NewSockets.end(); ++i)
{
WorldSocket* sock = (*i);
if (sock->IsClosed())
{
sock->RemoveReference();
--m_Connections;
}
else
m_Sockets.insert (sock);
}
m_NewSockets.clear();
}
virtual int svc()
{
DEBUG_LOG ("Network Thread Starting");
WorldDatabase.ThreadStart();
ACE_ASSERT (m_Reactor);
SocketSet::iterator i, t;
while (!m_Reactor->reactor_event_loop_done())
{
// dont be too smart to move this outside the loop
// the run_reactor_event_loop will modify interval
ACE_Time_Value interval (0, 10000);
if (m_Reactor->run_reactor_event_loop (interval) == -1)
break;
AddNewSockets();
for (i = m_Sockets.begin(); i != m_Sockets.end();)
{
if ((*i)->Update() == -1)
{
t = i;
++i;
(*t)->CloseSocket();
(*t)->RemoveReference();
--m_Connections;
m_Sockets.erase (t);
}
else
++i;
}
}
WorldDatabase.ThreadEnd();
DEBUG_LOG ("Network Thread Exitting");
return 0;
}
private:
typedef ACE_Atomic_Op<ACE_SYNCH_MUTEX, long> AtomicInt;
typedef std::set<WorldSocket*> SocketSet;
ACE_Reactor* m_Reactor;
AtomicInt m_Connections;
int m_ThreadId;
SocketSet m_Sockets;
SocketSet m_NewSockets;
ACE_Thread_Mutex m_NewSockets_Lock;
};
WorldSocketMgr::WorldSocketMgr() :
m_NetThreadsCount(0),
m_NetThreads(0),
m_SockOutKBuff(-1),
m_SockOutUBuff(65536),
m_UseNoDelay(true),
m_Acceptor (0)
{
}
WorldSocketMgr::~WorldSocketMgr()
{
if (m_NetThreads)
delete [] m_NetThreads;
if (m_Acceptor)
delete m_Acceptor;
}
int
WorldSocketMgr::StartReactiveIO (ACE_UINT16 port, const char* address)
{
m_UseNoDelay = sConfig.GetBoolDefault ("Network.TcpNodelay", true);
int num_threads = sConfig.GetIntDefault ("Network.Threads", 1);
if (num_threads <= 0)
{
sLog.outError ("Network.Threads is wrong in your config file");
return -1;
}
m_NetThreadsCount = static_cast<size_t> (num_threads + 1);
m_NetThreads = new ReactorRunnable[m_NetThreadsCount];
sLog.outBasic ("Max allowed socket connections %d", ACE::max_handles());
// -1 means use default
m_SockOutKBuff = sConfig.GetIntDefault ("Network.OutKBuff", -1);
m_SockOutUBuff = sConfig.GetIntDefault ("Network.OutUBuff", 65536);
if (m_SockOutUBuff <= 0)
{
sLog.outError ("Network.OutUBuff is wrong in your config file");
return -1;
}
WorldSocket::Acceptor *acc = new WorldSocket::Acceptor;
m_Acceptor = acc;
ACE_INET_Addr listen_addr (port, address);
if (acc->open(listen_addr, m_NetThreads[0].GetReactor(), ACE_NONBLOCK) == -1)
{
sLog.outError ("Failed to open acceptor ,check if the port is free");
return -1;
}
for (size_t i = 0; i < m_NetThreadsCount; ++i)
m_NetThreads[i].Start();
return 0;
}
int
WorldSocketMgr::StartNetwork (ACE_UINT16 port, const char* address)
{
if (!sLog.IsOutDebug())
ACE_Log_Msg::instance()->priority_mask (LM_ERROR, ACE_Log_Msg::PROCESS);
if (StartReactiveIO(port, address) == -1)
return -1;
return 0;
}
void
WorldSocketMgr::StopNetwork()
{
if (m_Acceptor)
{
WorldSocket::Acceptor* acc = dynamic_cast<WorldSocket::Acceptor*> (m_Acceptor);
if (acc)
acc->close();
}
if (m_NetThreadsCount != 0)
{
for (size_t i = 0; i < m_NetThreadsCount; ++i)
m_NetThreads[i].Stop();
}
Wait();
}
void
WorldSocketMgr::Wait()
{
if (m_NetThreadsCount != 0)
{
for (size_t i = 0; i < m_NetThreadsCount; ++i)
m_NetThreads[i].Wait();
}
}
int
WorldSocketMgr::OnSocketOpen (WorldSocket* sock)
{
// set some options here
if (m_SockOutKBuff >= 0)
{
if (sock->peer().set_option (SOL_SOCKET,
SO_SNDBUF,
(void*) & m_SockOutKBuff,
sizeof (int)) == -1 && errno != ENOTSUP)
{
sLog.outError ("WorldSocketMgr::OnSocketOpen set_option SO_SNDBUF");
return -1;
}
}
static const int ndoption = 1;
// Set TCP_NODELAY.
if (m_UseNoDelay)
{
if (sock->peer().set_option (ACE_IPPROTO_TCP,
TCP_NODELAY,
(void*)&ndoption,
sizeof (int)) == -1)
{
sLog.outError ("WorldSocketMgr::OnSocketOpen: peer().set_option TCP_NODELAY errno = %s", ACE_OS::strerror (errno));
return -1;
}
}
sock->m_OutBufferSize = static_cast<size_t> (m_SockOutUBuff);
// we skip the Acceptor Thread
size_t min = 1;
ACE_ASSERT (m_NetThreadsCount >= 1);
for (size_t i = 1; i < m_NetThreadsCount; ++i)
if (m_NetThreads[i].Connections() < m_NetThreads[min].Connections())
min = i;
return m_NetThreads[min].AddSocket (sock);
}
WorldSocketMgr*
WorldSocketMgr::Instance()
{
return ACE_Singleton<WorldSocketMgr,ACE_Thread_Mutex>::instance();
}
+80
View File
@@ -0,0 +1,80 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 Trinity <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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/** \addtogroup u2w User to World Communication
* @{
* \file WorldSocketMgr.h
* \author Derex <derex101@gmail.com>
*/
#ifndef __WORLDSOCKETMGR_H
#define __WORLDSOCKETMGR_H
#include <ace/Basic_Types.h>
#include <ace/Singleton.h>
#include <ace/Thread_Mutex.h>
class WorldSocket;
class ReactorRunnable;
class ACE_Event_Handler;
/// Manages all sockets connected to peers and network threads
class WorldSocketMgr
{
public:
friend class WorldSocket;
friend class ACE_Singleton<WorldSocketMgr,ACE_Thread_Mutex>;
/// Start network, listen at address:port .
int StartNetwork (ACE_UINT16 port, const char* address);
/// Stops all network threads, It will wait for all running threads .
void StopNetwork();
/// Wait untill all network threads have "joined" .
void Wait();
/// Make this class singleton .
static WorldSocketMgr* Instance();
private:
int OnSocketOpen(WorldSocket* sock);
int StartReactiveIO(ACE_UINT16 port, const char* address);
private:
WorldSocketMgr();
virtual ~WorldSocketMgr();
ReactorRunnable* m_NetThreads;
size_t m_NetThreadsCount;
int m_SockOutKBuff;
int m_SockOutUBuff;
bool m_UseNoDelay;
ACE_Event_Handler* m_Acceptor;
};
#define sWorldSocketMgr WorldSocketMgr::Instance()
#endif
/// @}