feat(Core): replace ACE network with Boost.Asio (#6574)

This commit is contained in:
Kargatum
2021-07-16 15:43:56 +07:00
committed by GitHub
parent 7449496bb5
commit 8568c4fb33
64 changed files with 3242 additions and 4712 deletions

View File

@@ -4,188 +4,319 @@
#include "ArenaSpectator.h"
#include "BattlegroundMgr.h"
#include "Chat.h"
#include "LFGMgr.h"
#include "ObjectMgr.h"
#include "Opcodes.h"
#include "Map.h"
#include "Pet.h"
#include "Player.h"
#include "ScriptMgr.h"
#include "SpellAuraEffects.h"
#include "SpellAuras.h"
#include "World.h"
namespace ArenaSpectator
bool ArenaSpectator::HandleSpectatorSpectateCommand(ChatHandler* handler, char const* args)
{
bool HandleSpectatorSpectateCommand(ChatHandler* handler, char const* args)
Player* player = handler->GetSession()->GetPlayer();
std::list<std::string> errors;
if (!*args)
{
Player* player = handler->GetSession()->GetPlayer();
std::list<std::string> errors;
if (!*args)
handler->SendSysMessage("Missing player name.");
return true;
}
if (player->IsSpectator())
{
if (player->FindMap() && player->FindMap()->IsBattleArena())
{
handler->SendSysMessage("Missing player name.");
HandleSpectatorWatchCommand(handler, args);
return true;
}
if (player->IsSpectator())
{
if (player->FindMap() && player->FindMap()->IsBattleArena())
handler->PSendSysMessage("You are already spectacting arena.");
return true;
}
if (player->getClass() == CLASS_DEATH_KNIGHT && player->GetMapId() == 609)
{
handler->PSendSysMessage("Death Knights can't spectate before finishing questline.");
return true;
}
std::string name = std::string(args);
Player* spectate = ObjectAccessor::FindPlayerByName(name);
if (!spectate)
{
handler->SendSysMessage("Requested player not found.");
return true;
}
if (spectate->IsSpectator())
{
handler->SendSysMessage("Requested player is a spectator.");
return true;
}
if (!spectate->FindMap() || !spectate->FindMap()->IsBattleArena())
{
handler->SendSysMessage("Requested player is not in arena.");
return true;
}
BattlegroundMap* bgmap = ((BattlegroundMap*)spectate->FindMap());
if (!bgmap->GetBG() || bgmap->GetBG()->GetStatus() == STATUS_WAIT_LEAVE)
{
handler->SendSysMessage("This arena battle has finished.");
return true;
}
if (player->IsBeingTeleported() || !player->IsInWorld())
errors.push_back("Can't use while being teleported.");
if (!player->FindMap() || player->FindMap()->Instanceable())
errors.push_back("Can't use while in instance, bg or arena.");
if (player->GetVehicle())
errors.push_back("Can't be on a vehicle.");
if (player->IsInCombat())
errors.push_back("Can't be in combat.");
if (player->isUsingLfg())
errors.push_back("Can't spectate while using LFG system.");
if (player->InBattlegroundQueue())
errors.push_back("Can't be queued for arena or bg.");
if (player->GetGroup())
errors.push_back("Can't be in a group.");
if (player->HasUnitState(UNIT_STATE_ISOLATED))
errors.push_back("Can't be isolated.");
if (player->m_mover != player)
errors.push_back("You must control yourself.");
if (player->IsInFlight())
errors.push_back("Can't be in flight.");
if (player->IsMounted())
errors.push_back("Dismount before spectating.");
if (!player->IsAlive())
errors.push_back("Must be alive.");
if (!player->m_Controlled.empty())
errors.push_back("Can't be controlling creatures.");
const Unit::VisibleAuraMap* va = player->GetVisibleAuras();
for (auto itr = va->begin(); itr != va->end(); ++itr)
if (Aura* aura = itr->second->GetBase())
if (!itr->second->IsPositive() && !aura->IsPermanent() && aura->GetDuration() < HOUR * IN_MILLISECONDS)
{
HandleSpectatorWatchCommand(handler, args);
return true;
}
handler->PSendSysMessage("You are already spectacting arena.");
return true;
}
if (player->getClass() == CLASS_DEATH_KNIGHT && player->GetMapId() == 609)
{
handler->PSendSysMessage("Death Knights can't spectate before finishing questline.");
return true;
}
std::string name = std::string(args);
Player* spectate = ObjectAccessor::FindPlayerByName(name);
if (!spectate)
{
handler->SendSysMessage("Requested player not found.");
return true;
}
if (spectate->IsSpectator())
{
handler->SendSysMessage("Requested player is a spectator.");
return true;
}
if (!spectate->FindMap() || !spectate->FindMap()->IsBattleArena())
{
handler->SendSysMessage("Requested player is not in arena.");
return true;
}
BattlegroundMap* bgmap = ((BattlegroundMap*)spectate->FindMap());
if (!bgmap->GetBG() || bgmap->GetBG()->GetStatus() == STATUS_WAIT_LEAVE)
{
handler->SendSysMessage("This arena battle has finished.");
return true;
}
if (player->IsBeingTeleported() || !player->IsInWorld())
errors.push_back("Can't use while being teleported.");
if (!player->FindMap() || player->FindMap()->Instanceable())
errors.push_back("Can't use while in instance, bg or arena.");
if (player->GetVehicle())
errors.push_back("Can't be on a vehicle.");
if (player->IsInCombat())
errors.push_back("Can't be in combat.");
if (player->isUsingLfg())
errors.push_back("Can't spectate while using LFG system.");
if (player->InBattlegroundQueue())
errors.push_back("Can't be queued for arena or bg.");
if (player->GetGroup())
errors.push_back("Can't be in a group.");
if (player->HasUnitState(UNIT_STATE_ISOLATED))
errors.push_back("Can't be isolated.");
if (player->m_mover != player)
errors.push_back("You must control yourself.");
if (player->IsInFlight())
errors.push_back("Can't be in flight.");
if (player->IsMounted())
errors.push_back("Dismount before spectating.");
if (!player->IsAlive())
errors.push_back("Must be alive.");
if (!player->m_Controlled.empty())
errors.push_back("Can't be controlling creatures.");
const Unit::VisibleAuraMap* va = player->GetVisibleAuras();
for (Unit::VisibleAuraMap::const_iterator itr = va->begin(); itr != va->end(); ++itr)
if (Aura* aura = itr->second->GetBase())
if (!itr->second->IsPositive() && !aura->IsPermanent() && aura->GetDuration() < HOUR * IN_MILLISECONDS)
switch (aura->GetSpellInfo()->Id)
{
switch (aura->GetSpellInfo()->Id)
{
case lfg::LFG_SPELL_DUNGEON_DESERTER:
case lfg::LFG_SPELL_DUNGEON_COOLDOWN:
case 26013: // bg deserter
case 57724: // sated
case 57723: // exhaustion
case 25771: // forbearance
case 15007: // resurrection sickness
case 24755: // Tricked or Treated (z eventu)
continue;
}
errors.push_back("Can't have negative auras.");
break;
case lfg::LFG_SPELL_DUNGEON_DESERTER:
case lfg::LFG_SPELL_DUNGEON_COOLDOWN:
case 26013: // bg deserter
case 57724: // sated
case 57723: // exhaustion
case 25771: // forbearance
case 15007: // resurrection sickness
case 24755: // Tricked or Treated (z eventu)
continue;
}
if (uint32 inviteInstanceId = player->GetPendingSpectatorInviteInstanceId())
{
if (Battleground* tbg = sBattlegroundMgr->GetBattleground(inviteInstanceId))
tbg->RemoveToBeTeleported(player->GetGUID());
player->SetPendingSpectatorInviteInstanceId(0);
}
bool bgPreparation = false;
if ((!handler->GetSession()->GetSecurity() && bgmap->GetBG()->GetStatus() != STATUS_IN_PROGRESS) ||
(handler->GetSession()->GetSecurity() && bgmap->GetBG()->GetStatus() != STATUS_WAIT_JOIN && bgmap->GetBG()->GetStatus() != STATUS_IN_PROGRESS))
{
bgPreparation = true;
handler->SendSysMessage("Arena is not in progress yet. You will be invited as soon as it starts.");
bgmap->GetBG()->AddToBeTeleported(player->GetGUID(), spectate->GetGUID());
player->SetPendingSpectatorInviteInstanceId(spectate->GetBattlegroundId());
}
if (!errors.empty())
{
handler->PSendSysMessage("To spectate, please fix the following:");
for (std::list<std::string>::const_iterator itr = errors.begin(); itr != errors.end(); ++itr)
handler->PSendSysMessage("- %s", (*itr).c_str());
return true;
}
if (bgPreparation)
return true;
player->SetPendingSpectatorForBG(spectate->GetBattlegroundId());
player->SetBattlegroundId(spectate->GetBattlegroundId(), spectate->GetBattlegroundTypeId(), PLAYER_MAX_BATTLEGROUND_QUEUES, false, false, TEAM_NEUTRAL);
player->SetEntryPoint();
float z = spectate->GetMapId() == 618 ? std::max(28.27f, spectate->GetPositionZ() + 0.25f) : spectate->GetPositionZ() + 0.25f;
player->TeleportTo(spectate->GetMapId(), spectate->GetPositionX(), spectate->GetPositionY(), z, spectate->GetOrientation(), TELE_TO_GM_MODE);
return true;
}
bool HandleSpectatorWatchCommand(ChatHandler* handler, char const* args)
{
if (!*args)
return true;
Player* player = handler->GetSession()->GetPlayer();
if (!player->IsSpectator())
return true;
if (!player->FindMap() || !player->FindMap()->IsBattleArena())
return true;
Battleground* bg = ((BattlegroundMap*)player->FindMap())->GetBG();
if (!bg || bg->GetStatus() != STATUS_IN_PROGRESS)
return true;
std::string name = std::string(args);
Player* spectate = ObjectAccessor::FindPlayerByName(name);
if (!spectate || !spectate->IsAlive() || spectate->IsSpectator() || spectate->GetGUID() == player->GetGUID() || !spectate->IsInWorld() || !spectate->FindMap() || spectate->IsBeingTeleported() || spectate->FindMap() != player->FindMap() || !bg->IsPlayerInBattleground(spectate->GetGUID()))
return true;
if (WorldObject* o = player->GetViewpoint())
if (Unit* u = o->ToUnit())
{
u->RemoveAurasByType(SPELL_AURA_BIND_SIGHT, player->GetGUID());
player->RemoveAurasDueToSpell(SPECTATOR_SPELL_BINDSIGHT, player->GetGUID(), (1 << EFFECT_1));
if (u->GetGUID() == spectate->GetGUID())
return true;
errors.push_back("Can't have negative auras.");
break;
}
if (player->GetGuidValue(PLAYER_FARSIGHT) || player->m_seer != player) // pussywizard: below this point we must not have a viewpoint!
return true;
if (uint32 inviteInstanceId = player->GetPendingSpectatorInviteInstanceId())
{
if (Battleground* tbg = sBattlegroundMgr->GetBattleground(inviteInstanceId))
tbg->RemoveToBeTeleported(player->GetGUID());
player->SetPendingSpectatorInviteInstanceId(0);
}
if (player->HaveAtClient(spectate))
player->CastSpell(spectate, SPECTATOR_SPELL_BINDSIGHT, true);
bool bgPreparation = false;
if ((!handler->GetSession()->GetSecurity() && bgmap->GetBG()->GetStatus() != STATUS_IN_PROGRESS) ||
(handler->GetSession()->GetSecurity() && bgmap->GetBG()->GetStatus() != STATUS_WAIT_JOIN && bgmap->GetBG()->GetStatus() != STATUS_IN_PROGRESS))
{
bgPreparation = true;
handler->SendSysMessage("Arena is not in progress yet. You will be invited as soon as it starts.");
bgmap->GetBG()->AddToBeTeleported(player->GetGUID(), spectate->GetGUID());
player->SetPendingSpectatorInviteInstanceId(spectate->GetBattlegroundId());
}
if (!errors.empty())
{
handler->PSendSysMessage("To spectate, please fix the following:");
for (std::list<std::string>::const_iterator itr = errors.begin(); itr != errors.end(); ++itr)
handler->PSendSysMessage("- %s", (*itr).c_str());
return true;
}
if (bgPreparation)
return true;
float z = spectate->GetMapId() == 618 ? std::max(28.27f, spectate->GetPositionZ() + 0.25f) : spectate->GetPositionZ() + 0.25f;
player->SetPendingSpectatorForBG(spectate->GetBattlegroundId());
player->SetBattlegroundId(spectate->GetBattlegroundId(), spectate->GetBattlegroundTypeId(), PLAYER_MAX_BATTLEGROUND_QUEUES, false, false, TEAM_NEUTRAL);
player->SetEntryPoint();
player->TeleportTo(spectate->GetMapId(), spectate->GetPositionX(), spectate->GetPositionY(), z, spectate->GetOrientation(), TELE_TO_GM_MODE);
return true;
}
bool ArenaSpectator::HandleSpectatorWatchCommand(ChatHandler* handler, char const* args)
{
if (!*args)
return true;
Player* player = handler->GetSession()->GetPlayer();
if (!player->IsSpectator())
return true;
if (!player->FindMap() || !player->FindMap()->IsBattleArena())
return true;
Battleground* bg = ((BattlegroundMap*)player->FindMap())->GetBG();
if (!bg || bg->GetStatus() != STATUS_IN_PROGRESS)
return true;
std::string name = std::string(args);
Player* spectate = ObjectAccessor::FindPlayerByName(name);
if (!spectate || !spectate->IsAlive() || spectate->IsSpectator() || spectate->GetGUID() == player->GetGUID() || !spectate->IsInWorld() || !spectate->FindMap() || spectate->IsBeingTeleported() || spectate->FindMap() != player->FindMap() || !bg->IsPlayerInBattleground(spectate->GetGUID()))
return true;
if (WorldObject* o = player->GetViewpoint())
if (Unit* u = o->ToUnit())
{
u->RemoveAurasByType(SPELL_AURA_BIND_SIGHT, player->GetGUID());
player->RemoveAurasDueToSpell(SPECTATOR_SPELL_BINDSIGHT, player->GetGUID(), (1 << EFFECT_1));
if (u->GetGUID() == spectate->GetGUID())
return true;
}
if (player->GetUInt64Value(PLAYER_FARSIGHT) || player->m_seer != player) // pussywizard: below this point we must not have a viewpoint!
return true;
if (player->HaveAtClient(spectate))
player->CastSpell(spectate, SPECTATOR_SPELL_BINDSIGHT, true);
return true;
}
void ArenaSpectator::CreatePacket(WorldPacket& data, std::string const& message)
{
size_t len = message.length();
data.Initialize(SMSG_MESSAGECHAT, 1 + 4 + 8 + 4 + 8 + 4 + 1 + len + 1);
data << uint8(CHAT_MSG_WHISPER);
data << uint32(LANG_ADDON);
data << uint64(0);
data << uint32(0);
data << uint64(0);
data << uint32(len + 1);
data << message;
data << uint8(0);
}
void ArenaSpectator::HandleResetCommand(Player* player)
{
if (!player->FindMap() || !player->IsInWorld() || !player->FindMap()->IsBattleArena())
return;
Battleground* bg = ((BattlegroundMap*)player->FindMap())->GetBG();
if (!bg || bg->GetStatus() != STATUS_IN_PROGRESS)
return;
Battleground::BattlegroundPlayerMap const& pl = bg->GetPlayers();
for (Battleground::BattlegroundPlayerMap::const_iterator itr = pl.begin(); itr != pl.end(); ++itr)
{
if (player->HasReceivedSpectatorResetFor(itr->first))
continue;
Player* plr = itr->second;
player->AddReceivedSpectatorResetFor(itr->first);
SendCommand_String(player, itr->first, "NME", plr->GetName().c_str());
// Xinef: addon compatibility
SendCommand_UInt32Value(player, itr->first, "TEM", plr->GetBgTeamId() == TEAM_ALLIANCE ? ALLIANCE : HORDE);
SendCommand_UInt32Value(player, itr->first, "CLA", plr->getClass());
SendCommand_UInt32Value(player, itr->first, "MHP", plr->GetMaxHealth());
SendCommand_UInt32Value(player, itr->first, "CHP", plr->GetHealth());
SendCommand_UInt32Value(player, itr->first, "STA", plr->IsAlive() ? 1 : 0);
Powers ptype = plr->getPowerType();
SendCommand_UInt32Value(player, itr->first, "PWT", ptype);
SendCommand_UInt32Value(player, itr->first, "MPW", ptype == POWER_RAGE || ptype == POWER_RUNIC_POWER ? plr->GetMaxPower(ptype) / 10 : plr->GetMaxPower(ptype));
SendCommand_UInt32Value(player, itr->first, "CPW", ptype == POWER_RAGE || ptype == POWER_RUNIC_POWER ? plr->GetPower(ptype) / 10 : plr->GetPower(ptype));
Pet* pet = plr->GetPet();
SendCommand_UInt32Value(player, itr->first, "PHP", pet && pet->GetCreatureTemplate()->family ? (uint32)pet->GetHealthPct() : 0);
SendCommand_UInt32Value(player, itr->first, "PET", pet ? pet->GetCreatureTemplate()->family : 0);
SendCommand_GUID(player, itr->first, "TRG", plr->GetTarget());
SendCommand_UInt32Value(player, itr->first, "RES", 1);
SendCommand_UInt32Value(player, itr->first, "CDC", 1);
SendCommand_UInt32Value(player, itr->first, "TIM", (bg->GetStartTime() < 46 * MINUTE * IN_MILLISECONDS) ? (46 * MINUTE * IN_MILLISECONDS - bg->GetStartTime()) / IN_MILLISECONDS : 0);
// "SPE" not here (only possible to send starting a new cast)
// send all "CD"
SpellCooldowns const& sc = plr->GetSpellCooldownMap();
for (SpellCooldowns::const_iterator itrc = sc.begin(); itrc != sc.end(); ++itrc)
if (itrc->second.sendToSpectator && itrc->second.maxduration >= SPECTATOR_COOLDOWN_MIN * IN_MILLISECONDS && itrc->second.maxduration <= SPECTATOR_COOLDOWN_MAX * IN_MILLISECONDS)
if (uint32 cd = (getMSTimeDiff(getMSTime(), itrc->second.end) / 1000))
SendCommand_Cooldown(player, itr->first, "ACD", itrc->first, cd, itrc->second.maxduration / 1000);
// send all visible "AUR"
Unit::VisibleAuraMap const* visibleAuras = plr->GetVisibleAuras();
for (Unit::VisibleAuraMap::const_iterator aitr = visibleAuras->begin(); aitr != visibleAuras->end(); ++aitr)
{
Aura* aura = aitr->second->GetBase();
if (ShouldSendAura(aura, aitr->second->GetEffectMask(), plr->GetGUID(), false))
SendCommand_Aura(player, itr->first, "AUR", aura->GetCasterGUID(), aura->GetSpellInfo()->Id, aura->GetSpellInfo()->IsPositive(), aura->GetSpellInfo()->Dispel, aura->GetDuration(), aura->GetMaxDuration(), (aura->GetCharges() > 1 ? aura->GetCharges() : aura->GetStackAmount()), false);
}
}
}
bool ArenaSpectator::ShouldSendAura(Aura* aura, uint8 effMask, ObjectGuid targetGUID, bool remove)
{
if (aura->GetSpellInfo()->SpellIconID == 1 || aura->GetSpellInfo()->HasAttribute(SPELL_ATTR1_NO_AURA_ICON))
return false;
if (remove || aura->GetSpellInfo()->HasAttribute(SPELL_ATTR0_CU_DONT_BREAK_STEALTH) || aura->GetSpellInfo()->SpellFamilyName == SPELLFAMILY_GENERIC)
return true;
for (uint8 i = EFFECT_0; i < MAX_SPELL_EFFECTS; ++i)
{
if (effMask & (1 << i))
{
AuraType at = aura->GetEffect(i)->GetAuraType();
if ((aura->GetEffect(i)->GetAmount() && (aura->GetSpellInfo()->IsPositive() || targetGUID != aura->GetCasterGUID())) ||
at == SPELL_AURA_MECHANIC_IMMUNITY || at == SPELL_AURA_EFFECT_IMMUNITY || at == SPELL_AURA_STATE_IMMUNITY || at == SPELL_AURA_SCHOOL_IMMUNITY || at == SPELL_AURA_DISPEL_IMMUNITY)
return true;
}
}
return false;
}
template<>
AC_GAME_API void ArenaSpectator::SendPacketTo(const Player* player, std::string&& message)
{
WorldPacket data;
CreatePacket(data, message);
player->GetSession()->SendPacket(&data);
}
template<>
AC_GAME_API void ArenaSpectator::SendPacketTo(const Map* map, std::string&& message)
{
if (!map->IsBattleArena())
return;
Battleground* bg = ((BattlegroundMap*)map)->GetBG();
if (!bg || bg->GetStatus() != STATUS_IN_PROGRESS)
return;
WorldPacket data;
CreatePacket(data, message);
bg->SpectatorsSendPacket(data);
}

View File

@@ -5,15 +5,17 @@
#ifndef AZEROTHCORE_ARENASPECTATOR_H
#define AZEROTHCORE_ARENASPECTATOR_H
#include "Battleground.h"
#include "Chat.h"
#include "LFGMgr.h"
#include "Map.h"
#include "Pet.h"
#include "Player.h"
#include "SpellAuraEffects.h"
#include "SpellAuras.h"
#include "World.h"
#include "Common.h"
#include "ObjectDefines.h"
#include "SpellInfo.h"
#include "SpellMgr.h"
#include "StringFormat.h"
class Aura;
class Player;
class Map;
class WorldPacket;
#define SPECTATOR_ADDON_VERSION 27
#define SPECTATOR_BUFFER_LEN 150
@@ -25,196 +27,78 @@
namespace ArenaSpectator
{
template<class T> inline void SendCommand(T* o, const char* format, ...) ATTR_PRINTF(2, 3);
inline void CreatePacket(WorldPacket& data, const char* m);
inline void SendPacketTo(const Player* p, const char* m);
inline void SendPacketTo(const Map* map, const char* m);
inline void HandleResetCommand(Player* p);
inline bool ShouldSendAura(Aura* aura, uint8 effMask, ObjectGuid targetGUID, bool remove);
template<class T> inline void SendCommand_String(T* p, ObjectGuid targetGUID, const char* prefix, const std::string& c);
template<class T> inline void SendCommand_UInt32Value(T* o, ObjectGuid targetGUID, const char* prefix, uint32 t);
template<class T> inline void SendCommand_GUID(T* o, ObjectGuid targetGUID, const char* prefix, ObjectGuid t);
template<class T> inline void SendCommand_Spell(T* o, ObjectGuid targetGUID, const char* prefix, uint32 id, int32 casttime);
template<class T> inline void SendCommand_Cooldown(T* o, ObjectGuid targetGUID, const char* prefix, uint32 id, uint32 dur, uint32 maxdur);
template<class T> inline void SendCommand_Aura(T* o, ObjectGuid targetGUID, const char* prefix, ObjectGuid caster, uint32 id, bool isDebuff, uint32 dispel, int32 dur, int32 maxdur, uint32 stack, bool remove);
bool HandleSpectatorSpectateCommand(ChatHandler* handler, char const* args);
bool HandleSpectatorWatchCommand(ChatHandler* handler, char const* args);
// definitions below:
template<class T>
void SendCommand(T* o, const char* format, ...)
{
if (!format)
return;
char buffer[SPECTATOR_BUFFER_LEN];
va_list ap;
va_start(ap, format);
vsnprintf(buffer, SPECTATOR_BUFFER_LEN, format, ap);
va_end(ap);
SendPacketTo(o, buffer);
}
AC_GAME_API void SendPacketTo(const T* object, std::string&& message);
void CreatePacket(WorldPacket& data, const char* m)
template<class T, typename Format, typename... Args>
inline void SendCommand(T* o, Format&& fmt, Args&& ... args)
{
size_t len = strlen(m);
data.Initialize(SMSG_MESSAGECHAT, 1 + 4 + 8 + 4 + 8 + 4 + 1 + len + 1);
data << uint8(CHAT_MSG_WHISPER);
data << uint32(LANG_ADDON);
data << uint64(0);
data << uint32(0);
data << uint64(0);
data << uint32(len + 1);
data << m;
data << uint8(0);
}
void SendPacketTo(const Player* p, const char* m)
{
WorldPacket data;
CreatePacket(data, m);
p->GetSession()->SendPacket(&data);
}
void SendPacketTo(const Map* map, const char* m)
{
if (!map->IsBattleArena())
return;
Battleground* bg = ((BattlegroundMap*)map)->GetBG();
if (!bg || bg->GetStatus() != STATUS_IN_PROGRESS)
return;
WorldPacket data;
CreatePacket(data, m);
bg->SpectatorsSendPacket(data);
SendPacketTo(o, Acore::StringFormat(std::forward<Format>(fmt), std::forward<Args>(args)...));
}
template<class T>
void SendCommand_String(T* o, ObjectGuid targetGUID, const char* prefix, const char* c)
inline void SendCommand_String(T* o, ObjectGuid targetGUID, const char* prefix, const char* c)
{
if (!targetGUID.IsPlayer())
return;
SendCommand(o, "%s0x%016llX;%s=%s;", SPECTATOR_ADDON_PREFIX, (unsigned long long)targetGUID.GetRawValue(), prefix, c);
SendCommand(o, "%s0x%016llX;%s=%s;", SPECTATOR_ADDON_PREFIX, targetGUID.GetRawValue(), prefix, c);
}
template<class T>
void SendCommand_UInt32Value(T* o, ObjectGuid targetGUID, const char* prefix, uint32 t)
inline void SendCommand_UInt32Value(T* o, ObjectGuid targetGUID, const char* prefix, uint32 t)
{
if (!targetGUID.IsPlayer())
return;
SendCommand(o, "%s0x%016llX;%s=%u;", SPECTATOR_ADDON_PREFIX, (unsigned long long)targetGUID.GetRawValue(), prefix, t);
SendCommand(o, "%s0x%016llX;%s=%u;", SPECTATOR_ADDON_PREFIX, targetGUID.GetRawValue(), prefix, t);
}
template<class T>
void SendCommand_GUID(T* o, ObjectGuid targetGUID, const char* prefix, ObjectGuid t)
inline void SendCommand_GUID(T* o, ObjectGuid targetGUID, const char* prefix, ObjectGuid t)
{
if (!targetGUID.IsPlayer())
return;
SendCommand(o, "%s0x%016llX;%s=0x%016llX;", SPECTATOR_ADDON_PREFIX, (unsigned long long)targetGUID.GetRawValue(), prefix, (unsigned long long)t.GetRawValue());
SendCommand(o, "%s0x%016llX;%s=0x%016llX;", SPECTATOR_ADDON_PREFIX, targetGUID.GetRawValue(), prefix, t.GetRawValue());
}
template<class T>
void SendCommand_Spell(T* o, ObjectGuid targetGUID, const char* prefix, uint32 id, int32 casttime)
inline void SendCommand_Spell(T* o, ObjectGuid targetGUID, const char* prefix, uint32 id, int32 casttime)
{
if (!targetGUID.IsPlayer())
return;
SendCommand(o, "%s0x%016llX;%s=%u,%i;", SPECTATOR_ADDON_PREFIX, (unsigned long long)targetGUID.GetRawValue(), prefix, id, casttime);
SendCommand(o, "%s0x%016llX;%s=%u,%i;", SPECTATOR_ADDON_PREFIX, targetGUID.GetRawValue(), prefix, id, casttime);
}
template<class T>
void SendCommand_Cooldown(T* o, ObjectGuid targetGUID, const char* prefix, uint32 id, uint32 dur, uint32 maxdur)
inline void SendCommand_Cooldown(T* o, ObjectGuid targetGUID, const char* prefix, uint32 id, uint32 dur, uint32 maxdur)
{
if (!targetGUID.IsPlayer())
return;
if (const SpellInfo* si = sSpellMgr->GetSpellInfo(id))
if (si->SpellIconID == 1)
return;
SendCommand(o, "%s0x%016llX;%s=%u,%u,%u;", SPECTATOR_ADDON_PREFIX, (unsigned long long)targetGUID.GetRawValue(), prefix, id, dur, maxdur);
SendCommand(o, "%s0x%016llX;%s=%u,%u,%u;", SPECTATOR_ADDON_PREFIX, targetGUID.GetRawValue(), prefix, id, dur, maxdur);
}
template<class T>
void SendCommand_Aura(T* o, ObjectGuid targetGUID, const char* prefix, ObjectGuid caster, uint32 id, bool isDebuff, uint32 dispel, int32 dur, int32 maxdur, uint32 stack, bool remove)
inline void SendCommand_Aura(T* o, ObjectGuid targetGUID, const char* prefix, ObjectGuid caster, uint32 id, bool isDebuff, uint32 dispel, int32 dur, int32 maxdur, uint32 stack, bool remove)
{
if (!targetGUID.IsPlayer())
return;
SendCommand(o, "%s0x%016llX;%s=%u,%u,%i,%i,%u,%u,%u,0x%016llX;", SPECTATOR_ADDON_PREFIX, (unsigned long long)targetGUID.GetRawValue(), prefix, remove ? 1 : 0, stack, dur, maxdur, id, dispel, isDebuff ? 1 : 0, (unsigned long long)caster.GetRawValue());
SendCommand(o, "%s0x%016llX;%s=%u,%u,%i,%i,%u,%u,%u,0x%016llX;", SPECTATOR_ADDON_PREFIX, targetGUID.GetRawValue(), prefix, remove ? 1 : 0, stack, dur, maxdur, id, dispel, isDebuff ? 1 : 0, caster.GetRawValue());
}
void HandleResetCommand(Player* p)
{
if (!p->FindMap() || !p->IsInWorld() || !p->FindMap()->IsBattleArena())
return;
Battleground* bg = ((BattlegroundMap*)p->FindMap())->GetBG();
if (!bg || bg->GetStatus() != STATUS_IN_PROGRESS)
return;
Battleground::BattlegroundPlayerMap const& pl = bg->GetPlayers();
for (Battleground::BattlegroundPlayerMap::const_iterator itr = pl.begin(); itr != pl.end(); ++itr)
{
if (p->HasReceivedSpectatorResetFor(itr->first))
continue;
Player* plr = itr->second;
p->AddReceivedSpectatorResetFor(itr->first);
SendCommand_String(p, itr->first, "NME", plr->GetName().c_str());
// Xinef: addon compatibility
SendCommand_UInt32Value(p, itr->first, "TEM", plr->GetBgTeamId() == TEAM_ALLIANCE ? ALLIANCE : HORDE);
SendCommand_UInt32Value(p, itr->first, "CLA", plr->getClass());
SendCommand_UInt32Value(p, itr->first, "MHP", plr->GetMaxHealth());
SendCommand_UInt32Value(p, itr->first, "CHP", plr->GetHealth());
SendCommand_UInt32Value(p, itr->first, "STA", plr->IsAlive() ? 1 : 0);
Powers ptype = plr->getPowerType();
SendCommand_UInt32Value(p, itr->first, "PWT", ptype);
SendCommand_UInt32Value(p, itr->first, "MPW", ptype == POWER_RAGE || ptype == POWER_RUNIC_POWER ? plr->GetMaxPower(ptype) / 10 : plr->GetMaxPower(ptype));
SendCommand_UInt32Value(p, itr->first, "CPW", ptype == POWER_RAGE || ptype == POWER_RUNIC_POWER ? plr->GetPower(ptype) / 10 : plr->GetPower(ptype));
Pet* pet = plr->GetPet();
SendCommand_UInt32Value(p, itr->first, "PHP", pet && pet->GetCreatureTemplate()->family ? (uint32)pet->GetHealthPct() : 0);
SendCommand_UInt32Value(p, itr->first, "PET", pet ? pet->GetCreatureTemplate()->family : 0);
SendCommand_GUID(p, itr->first, "TRG", plr->GetTarget());
SendCommand_UInt32Value(p, itr->first, "RES", 1);
SendCommand_UInt32Value(p, itr->first, "CDC", 1);
SendCommand_UInt32Value(p, itr->first, "TIM", (bg->GetStartTime() < 46 * MINUTE * IN_MILLISECONDS) ? (46 * MINUTE * IN_MILLISECONDS - bg->GetStartTime()) / IN_MILLISECONDS : 0);
// "SPE" not here (only possible to send starting a new cast)
// send all "CD"
SpellCooldowns const& sc = plr->GetSpellCooldownMap();
for (SpellCooldowns::const_iterator itrc = sc.begin(); itrc != sc.end(); ++itrc)
if (itrc->second.sendToSpectator && itrc->second.maxduration >= SPECTATOR_COOLDOWN_MIN * IN_MILLISECONDS && itrc->second.maxduration <= SPECTATOR_COOLDOWN_MAX * IN_MILLISECONDS)
if (uint32 cd = (getMSTimeDiff(World::GetGameTimeMS(), itrc->second.end) / 1000))
SendCommand_Cooldown(p, itr->first, "ACD", itrc->first, cd, itrc->second.maxduration / 1000);
// send all visible "AUR"
Unit::VisibleAuraMap const* visibleAuras = plr->GetVisibleAuras();
for (Unit::VisibleAuraMap::const_iterator aitr = visibleAuras->begin(); aitr != visibleAuras->end(); ++aitr)
{
Aura* aura = aitr->second->GetBase();
if (ShouldSendAura(aura, aitr->second->GetEffectMask(), plr->GetGUID(), false))
SendCommand_Aura(p, itr->first, "AUR", aura->GetCasterGUID(), aura->GetSpellInfo()->Id, aura->GetSpellInfo()->IsPositive(), aura->GetSpellInfo()->Dispel, aura->GetDuration(), aura->GetMaxDuration(), (aura->GetCharges() > 1 ? aura->GetCharges() : aura->GetStackAmount()), false);
}
}
}
bool ShouldSendAura(Aura* aura, uint8 effMask, ObjectGuid targetGUID, bool remove)
{
if (aura->GetSpellInfo()->SpellIconID == 1 || aura->GetSpellInfo()->HasAttribute(SPELL_ATTR1_NO_AURA_ICON))
return false;
if (remove || aura->GetSpellInfo()->HasAttribute(SPELL_ATTR0_CU_AURA_CC) || aura->GetSpellInfo()->SpellFamilyName == SPELLFAMILY_GENERIC)
return true;
for(uint8 i = EFFECT_0; i < MAX_SPELL_EFFECTS; ++i)
{
if (effMask & (1 << i))
{
AuraType at = aura->GetEffect(i)->GetAuraType();
if ((aura->GetEffect(i)->GetAmount() && (aura->GetSpellInfo()->IsPositive() || targetGUID != aura->GetCasterGUID())) ||
at == SPELL_AURA_MECHANIC_IMMUNITY || at == SPELL_AURA_EFFECT_IMMUNITY || at == SPELL_AURA_STATE_IMMUNITY || at == SPELL_AURA_SCHOOL_IMMUNITY || at == SPELL_AURA_DISPEL_IMMUNITY)
return true;
}
}
return false;
}
AC_GAME_API bool HandleSpectatorSpectateCommand(ChatHandler* handler, char const* args);
AC_GAME_API bool HandleSpectatorWatchCommand(ChatHandler* handler, char const* args);
AC_GAME_API void CreatePacket(WorldPacket& data, std::string const& message);
AC_GAME_API void HandleResetCommand(Player* player);
AC_GAME_API bool ShouldSendAura(Aura* aura, uint8 effMask, ObjectGuid targetGUID, bool remove);
}
#endif

View File

@@ -4,6 +4,7 @@
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*/
#include "Pet.h"
#include "ArenaSpectator.h"
#include "Battleground.h"
#include "Common.h"
@@ -13,7 +14,7 @@
#include "InstanceScript.h"
#include "Log.h"
#include "ObjectMgr.h"
#include "Pet.h"
#include "Player.h"
#include "ScriptMgr.h"
#include "SpellAuraEffects.h"
#include "SpellAuras.h"

View File

@@ -58,7 +58,6 @@
#include "QuestDef.h"
#include "QueryHolder.h"
#include "ReputationMgr.h"
#include "revision.h"
#include "Realm.h"
#include "SavingSystem.h"
#include "ScriptMgr.h"

View File

@@ -12,7 +12,7 @@
#include "Player.h"
#include "PoolMgr.h"
#include "ReputationMgr.h"
#include "revision.h"
#include "GitRevision.h"
#include "ScriptMgr.h"
#include "SpellAuraEffects.h"
@@ -559,8 +559,8 @@ void Player::AddQuest(Quest const* quest, Object* questGiver)
auto stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_QUEST_TRACK);
stmt->setUInt32(0, quest_id);
stmt->setUInt32(1, GetGUID().GetCounter());
stmt->setString(2, _HASH);
stmt->setString(3, _DATE);
stmt->setString(2, GitRevision::GetHash());
stmt->setString(3, GitRevision::GetDate());
// add to Quest Tracker
CharacterDatabase.Execute(stmt);

View File

@@ -3884,6 +3884,7 @@ void Unit::HandleSafeUnitPointersOnDelete(Unit* thisUnit)
return;
for (std::set<SafeUnitPointer*>::iterator itr = thisUnit->SafeUnitPointerSet.begin(); itr != thisUnit->SafeUnitPointerSet.end(); ++itr)
(*itr)->UnitDeleted();
thisUnit->SafeUnitPointerSet.clear();
}

View File

@@ -4,8 +4,6 @@
//add here most rarely modified headers to speed up debug build compilation
#include "WorldSocket.h" // must be first to make ACE happy with ACE includes in it
#include "Common.h"
#include "Log.h"
#include "MapManager.h"
@@ -14,3 +12,4 @@
#include "ObjectMgr.h"
#include "Opcodes.h"
#include "SharedDefines.h"
#include "WorldSocket.h"

View File

@@ -345,18 +345,18 @@ void ScriptMgr::OnNetworkStop()
FOREACH_SCRIPT(ServerScript)->OnNetworkStop();
}
void ScriptMgr::OnSocketOpen(WorldSocket* socket)
void ScriptMgr::OnSocketOpen(std::shared_ptr<WorldSocket> socket)
{
ASSERT(socket);
FOREACH_SCRIPT(ServerScript)->OnSocketOpen(socket);
}
void ScriptMgr::OnSocketClose(WorldSocket* socket, bool wasNew)
void ScriptMgr::OnSocketClose(std::shared_ptr<WorldSocket> socket)
{
ASSERT(socket);
FOREACH_SCRIPT(ServerScript)->OnSocketClose(socket, wasNew);
FOREACH_SCRIPT(ServerScript)->OnSocketClose(socket);
}
void ScriptMgr::OnPacketReceive(WorldSession* session, WorldPacket const& packet)

View File

@@ -144,11 +144,11 @@ public:
virtual void OnNetworkStop() { }
// Called when a remote socket establishes a connection to the server. Do not store the socket object.
virtual void OnSocketOpen(WorldSocket* /*socket*/) { }
virtual void OnSocketOpen(std::shared_ptr<WorldSocket> /*socket*/) { }
// Called when a socket is closed. Do not store the socket object, and do not rely on the connection
// being open; it is not.
virtual void OnSocketClose(WorldSocket* /*socket*/, bool /*wasNew*/) { }
virtual void OnSocketClose(std::shared_ptr<WorldSocket> /*socket*/) { }
// Called when a packet is sent to a client. The packet object is a copy of the original packet, so reading
// and modifying it is safe.
@@ -1456,8 +1456,8 @@ public: /* SpellScriptLoader */
public: /* ServerScript */
void OnNetworkStart();
void OnNetworkStop();
void OnSocketOpen(WorldSocket* socket);
void OnSocketClose(WorldSocket* socket, bool wasNew);
void OnSocketOpen(std::shared_ptr<WorldSocket> socket);
void OnSocketClose(std::shared_ptr<WorldSocket> socket);
void OnPacketReceive(WorldSession* session, WorldPacket const& packet);
void OnPacketSend(WorldSession* session, WorldPacket const& packet);

View File

@@ -31,7 +31,7 @@ bool WorldPackets::Strings::Utf8::Validate(std::string const& value)
//bool WorldPackets::Strings::Hyperlinks::Validate(std::string const& value)
//{
// if (!Warhead::Hyperlinks::CheckAllLinks(value))
// if (!Acore::Hyperlinks::CheckAllLinks(value))
// throw InvalidHyperlinkException(value);
//
// return true;

View File

@@ -1,17 +1,18 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2008-2021 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*/
#include "PacketLog.h"
#include "Config.h"
#include "IpAddress.h"
#include "Timer.h"
#include "WorldPacket.h"
#pragma pack(push, 1)
// Packet logging structures in PKT 3.1 format
// Packet logging structures in PKT 3.1 format
struct LogHeader
{
char Signature[3];
@@ -27,11 +28,19 @@ struct LogHeader
struct PacketHeader
{
char Direction[4];
// used to uniquely identify a connection
struct OptionalData
{
uint8 SocketIPBytes[16];
uint32 SocketPort;
};
uint32 Direction;
uint32 ConnectionId;
uint32 ArrivalTicks;
uint32 OptionalDataSize;
uint32 Length;
OptionalData OptionalData;
uint32 Opcode;
};
@@ -45,7 +54,9 @@ PacketLog::PacketLog() : _file(nullptr)
PacketLog::~PacketLog()
{
if (_file)
{
fclose(_file);
}
_file = nullptr;
}
@@ -60,9 +71,10 @@ void PacketLog::Initialize()
{
std::string logsDir = sConfigMgr->GetOption<std::string>("LogsDir", "");
if (!logsDir.empty())
if ((logsDir.at(logsDir.length() - 1) != '/') && (logsDir.at(logsDir.length() - 1) != '\\'))
logsDir.push_back('/');
if (!logsDir.empty() && (logsDir.at(logsDir.length() - 1) != '/') && (logsDir.at(logsDir.length() - 1) != '\\'))
{
logsDir.push_back('/');
}
std::string logname = sConfigMgr->GetOption<std::string>("PacketLogFile", "");
if (!logname.empty())
@@ -80,23 +92,42 @@ void PacketLog::Initialize()
header.SniffStartTicks = getMSTime();
header.OptionalDataSize = 0;
fwrite(&header, sizeof(header), 1, _file);
if (CanLogPacket())
{
fwrite(&header, sizeof(header), 1, _file);
}
}
}
void PacketLog::LogPacket(WorldPacket const& packet, Direction direction)
void PacketLog::LogPacket(WorldPacket const& packet, Direction direction, boost::asio::ip::address const& addr, uint16 port)
{
std::lock_guard<std::mutex> lock(_logPacketLock);
PacketHeader header;
*reinterpret_cast<uint32*>(header.Direction) = direction == CLIENT_TO_SERVER ? 0x47534d43 : 0x47534d53;
header.Direction = direction == CLIENT_TO_SERVER ? 0x47534d43 : 0x47534d53;
header.ConnectionId = 0;
header.ArrivalTicks = getMSTime();
header.OptionalDataSize = 0;
header.Length = packet.size() + 4;
header.OptionalDataSize = sizeof(header.OptionalData);
memset(header.OptionalData.SocketIPBytes, 0, sizeof(header.OptionalData.SocketIPBytes));
if (addr.is_v4())
{
auto bytes = addr.to_v4().to_bytes();
memcpy(header.OptionalData.SocketIPBytes, bytes.data(), bytes.size());
}
else if (addr.is_v6())
{
auto bytes = addr.to_v6().to_bytes();
memcpy(header.OptionalData.SocketIPBytes, bytes.data(), bytes.size());
}
header.OptionalData.SocketPort = port;
header.Length = packet.size() + sizeof(header.Opcode);
header.Opcode = packet.GetOpcode();
fwrite(&header, sizeof(header), 1, _file);
if (!packet.empty())
{
fwrite(packet.contents(), 1, packet.size(), _file);

View File

@@ -8,6 +8,7 @@
#define ACORE_PACKETLOG_H
#include "Common.h"
#include <boost/asio/ip/address.hpp>
#include <mutex>
enum Direction
@@ -18,7 +19,7 @@ enum Direction
class WorldPacket;
class PacketLog
class AC_GAME_API PacketLog
{
private:
PacketLog();
@@ -31,7 +32,7 @@ public:
void Initialize();
bool CanLogPacket() const { return (_file != nullptr); }
void LogPacket(WorldPacket const& packet, Direction direction);
void LogPacket(WorldPacket const& packet, Direction direction, boost::asio::ip::address const& addr, uint16 port);
private:
FILE* _file;

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#ifndef __SERVERPKTHDR_H__
#define __SERVERPKTHDR_H__
#include "Log.h"
#pragma pack(push, 1)
struct ServerPktHeader
{
/**
* size is the length of the payload _plus_ the length of the opcode
*/
ServerPktHeader(uint32 size, uint16 cmd) : size(size)
{
uint8 headerIndex=0;
if (isLargePacket())
{
LOG_DEBUG("network", "initializing large server to client packet. Size: %u, cmd: %u", size, cmd);
header[headerIndex++] = 0x80 | (0xFF & (size >> 16));
}
header[headerIndex++] = 0xFF &(size >> 8);
header[headerIndex++] = 0xFF & size;
header[headerIndex++] = 0xFF & cmd;
header[headerIndex++] = 0xFF & (cmd >> 8);
}
uint8 getHeaderLength()
{
// cmd = 2 bytes, size= 2||3bytes
return 2 + (isLargePacket() ? 3 : 2);
}
bool isLargePacket() const
{
return size > 0x7FFF;
}
const uint32 size;
uint8 header[5];
};
#pragma pack(pop)
#endif

View File

@@ -8,7 +8,7 @@
\ingroup u2w
*/
#include "WorldSocket.h" // must be first to make ACE happy with ACE includes in it
#include "WorldSession.h"
#include "AccountMgr.h"
#include "BattlegroundMgr.h"
#include "Common.h"
@@ -25,6 +25,7 @@
#include "PacketUtilities.h"
#include "Pet.h"
#include "Player.h"
#include "QueryHolder.h"
#include "SavingSystem.h"
#include "ScriptMgr.h"
#include "SocialMgr.h"
@@ -34,9 +35,8 @@
#include "WardenWin.h"
#include "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "QueryHolder.h"
#include "zlib.h"
#include "WorldSocket.h"
#include <zlib.h>
#ifdef ELUNA
#include "LuaEngine.h"
@@ -91,7 +91,8 @@ bool WorldSessionFilter::Process(WorldPacket* packet)
}
/// WorldSession constructor
WorldSession::WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter, bool skipQueue, uint32 TotalTime) :
WorldSession::WorldSession(uint32 id, std::string&& name, std::shared_ptr<WorldSocket> sock, AccountTypes sec, uint8 expansion,
time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter, bool skipQueue, uint32 TotalTime) :
m_muteTime(mute_time),
m_timeOutTime(0),
_lastAuctionListItemsMSTime(0),
@@ -103,6 +104,7 @@ WorldSession::WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8
_security(sec),
_skipQueue(skipQueue),
_accountId(id),
_accountName(std::move(name)),
m_expansion(expansion),
m_total_time(TotalTime),
_logoutTime(0),
@@ -125,7 +127,6 @@ WorldSession::WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8
{
memset(m_Tutorials, 0, sizeof(m_Tutorials));
_warden = nullptr;
_offlineTime = 0;
_kicked = false;
_shouldSetOfflineInDB = true;
@@ -135,10 +136,9 @@ WorldSession::WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8
if (sock)
{
m_Address = sock->GetRemoteAddress();
sock->AddReference();
m_Address = sock->GetRemoteIpAddress().to_string();
ResetTimeOutTime(false);
LoginDatabase.PExecute("UPDATE account SET online = 1 WHERE id = %u;", GetAccountId());
LoginDatabase.PExecute("UPDATE account SET online = 1 WHERE id = %u;", GetAccountId()); // One-time query
}
}
@@ -154,17 +154,10 @@ WorldSession::~WorldSession()
/// - If have unclosed socket, close it
if (m_Socket)
{
m_Socket->CloseSocket("WorldSession destructor");
m_Socket->RemoveReference();
m_Socket->CloseSocket();
m_Socket = nullptr;
}
if (_warden)
{
delete _warden;
_warden = nullptr;
}
///- empty incoming packet queue
WorldPacket* packet = nullptr;
while (_recvQueue.next(packet))
@@ -257,9 +250,7 @@ void WorldSession::SendPacket(WorldPacket const* packet)
#endif
LOG_TRACE("network.opcode", "S->C: %s %s", GetPlayerInfo().c_str(), GetOpcodeNameForLogging(static_cast<OpcodeServer>(packet->GetOpcode())).c_str());
if (m_Socket->SendPacket(*packet) == -1)
m_Socket->CloseSocket("m_Socket->SendPacket(*packet) == -1");
m_Socket->SendPacket(*packet);
}
/// Add an incoming packet to the queue
@@ -290,26 +281,28 @@ void WorldSession::LogUnprocessedTail(WorldPacket* packet)
/// Update the WorldSession (triggered by World update)
bool WorldSession::Update(uint32 diff, PacketFilter& updater)
{
if (updater.ProcessUnsafe())
{
UpdateTimeOutTime(diff);
///- Before we process anything:
/// If necessary, kick the player because the client didn't send anything for too long
/// (or they've been idling in character select)
if (sWorld->getBoolConfig(CONFIG_CLOSE_IDLE_CONNECTIONS) && IsConnectionIdle() && m_Socket)
m_Socket->CloseSocket();
/// If necessary, kick the player because the client didn't send anything for too long
/// (or they've been idling in character select)
if (sWorld->getBoolConfig(CONFIG_CLOSE_IDLE_CONNECTIONS) && IsConnectionIdle() && m_Socket)
m_Socket->CloseSocket("Client didn't send anything for too long");
}
if (updater.ProcessUnsafe())
UpdateTimeOutTime(diff);
HandleTeleportTimeout(updater.ProcessUnsafe());
uint32 _startMSTime = getMSTime();
///- Retrieve packets from the receive queue and call the appropriate handlers
/// not process packets if socket already closed
WorldPacket* packet = nullptr;
//! Delete packet after processing by default
bool deletePacket = true;
WorldPacket* firstDelayedPacket = nullptr;
std::vector<WorldPacket*> requeuePackets;
uint32 processedPackets = 0;
time_t currentTime = time(nullptr);
while (m_Socket && !m_Socket->IsClosed() && !_recvQueue.empty() && _recvQueue.peek(true) != firstDelayedPacket && _recvQueue.next(packet, updater))
while (m_Socket && _recvQueue.next(packet, updater))
{
OpcodeClient opcode = static_cast<OpcodeClient>(packet->GetOpcode());
ClientOpcodeHandler const* opHandle = opcodeTable[opcode];
@@ -412,38 +405,9 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
//Any leftover will be processed in next update
if (processedPackets > MAX_PROCESSED_PACKETS_IN_SAME_WORLDSESSION_UPDATE)
break;
if (getMSTimeDiff(_startMSTime, getMSTime()) >= 3) // limit (by time) packets processed in one update, prevent DDoS
break;
}
if (m_Socket && !m_Socket->IsClosed())
ProcessQueryCallbacks();
if (updater.ProcessUnsafe())
{
if (m_Socket && !m_Socket->IsClosed() && _warden)
{
_warden->Update(diff);
}
time_t currTime = time(nullptr);
if (ShouldLogOut(currTime) && !m_playerLoading)
{
LogoutPlayer(true);
}
if (m_Socket && m_Socket->IsClosed())
{
m_Socket->RemoveReference();
m_Socket = nullptr;
}
if (!m_Socket)
{
return false;
}
}
_recvQueue.readd(requeuePackets.begin(), requeuePackets.end());
if (!updater.ProcessUnsafe()) // <=> updater is of type MapSessionFilter
{
@@ -461,29 +425,60 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
}
}
ProcessQueryCallbacks();
//check if we are safe to proceed with logout
//logout procedure should happen only in World::UpdateSessions() method!!!
if (updater.ProcessUnsafe())
{
if (m_Socket && m_Socket->IsOpen() && _warden)
{
_warden->Update(diff);
}
if (ShouldLogOut(currentTime) && !m_playerLoading)
{
LogoutPlayer(true);
}
if (m_Socket && !m_Socket->IsOpen())
{
if (GetPlayer() && _warden)
_warden->Update(diff);
m_Socket = nullptr;
}
if (!m_Socket)
{
return false;
}
}
return true;
}
bool WorldSession::HandleSocketClosed()
{
if (m_Socket && m_Socket->IsClosed() && !IsKicked() && GetPlayer() && !PlayerLogout() && GetPlayer()->m_taxi.empty() && GetPlayer()->IsInWorld() && !World::IsStopped())
if (m_Socket && !m_Socket->IsOpen() && !IsKicked() && GetPlayer() && !PlayerLogout() && GetPlayer()->m_taxi.empty() && GetPlayer()->IsInWorld() && !World::IsStopped())
{
m_Socket->RemoveReference();
m_Socket = nullptr;
GetPlayer()->TradeCancel(false);
return true;
}
return false;
}
bool WorldSession::IsSocketClosed() const {
return !m_Socket || m_Socket->IsClosed();
bool WorldSession::IsSocketClosed() const
{
return !m_Socket || !m_Socket->IsOpen();
}
void WorldSession::HandleTeleportTimeout(bool updateInSessions)
{
// pussywizard: handle teleport ack timeout
if (m_Socket && !m_Socket->IsClosed() && GetPlayer() && GetPlayer()->IsBeingTeleported())
if (m_Socket && m_Socket->IsOpen() && GetPlayer() && GetPlayer()->IsBeingTeleported())
{
time_t currTime = time(nullptr);
if (updateInSessions) // session update from World::UpdateSessions
@@ -681,7 +676,12 @@ void WorldSession::LogoutPlayer(bool save)
void WorldSession::KickPlayer(std::string const& reason, bool setKicked)
{
if (m_Socket)
m_Socket->CloseSocket(reason);
{
LOG_INFO("network.kick", "Account: %u Character: '%s' %s kicked with reason: %s", GetAccountId(), _player ? _player->GetName().c_str() : "<none>",
_player ? _player->GetGUID().ToString().c_str() : "", reason.c_str());
m_Socket->CloseSocket();
}
if (setKicked)
SetKicked(true); // pussywizard: the session won't be left ingame for 60 seconds and to also kick offline session
@@ -947,41 +947,41 @@ void WorldSession::ReadMovementInfo(WorldPacket& data, MovementInfo* mi)
It will freeze clients that receive this player's movement info.
*/
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_ROOT),
MOVEMENTFLAG_ROOT);
MOVEMENTFLAG_ROOT);
//! Cannot hover without SPELL_AURA_HOVER
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_HOVER) && !GetPlayer()->HasAuraType(SPELL_AURA_HOVER),
MOVEMENTFLAG_HOVER);
MOVEMENTFLAG_HOVER);
//! Cannot ascend and descend at the same time
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_ASCENDING) && mi->HasMovementFlag(MOVEMENTFLAG_DESCENDING),
MOVEMENTFLAG_ASCENDING | MOVEMENTFLAG_DESCENDING);
MOVEMENTFLAG_ASCENDING | MOVEMENTFLAG_DESCENDING);
//! Cannot move left and right at the same time
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_LEFT) && mi->HasMovementFlag(MOVEMENTFLAG_RIGHT),
MOVEMENTFLAG_LEFT | MOVEMENTFLAG_RIGHT);
MOVEMENTFLAG_LEFT | MOVEMENTFLAG_RIGHT);
//! Cannot strafe left and right at the same time
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_STRAFE_LEFT) && mi->HasMovementFlag(MOVEMENTFLAG_STRAFE_RIGHT),
MOVEMENTFLAG_STRAFE_LEFT | MOVEMENTFLAG_STRAFE_RIGHT);
MOVEMENTFLAG_STRAFE_LEFT | MOVEMENTFLAG_STRAFE_RIGHT);
//! Cannot pitch up and down at the same time
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_PITCH_UP) && mi->HasMovementFlag(MOVEMENTFLAG_PITCH_DOWN),
MOVEMENTFLAG_PITCH_UP | MOVEMENTFLAG_PITCH_DOWN);
MOVEMENTFLAG_PITCH_UP | MOVEMENTFLAG_PITCH_DOWN);
//! Cannot move forwards and backwards at the same time
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_FORWARD) && mi->HasMovementFlag(MOVEMENTFLAG_BACKWARD),
MOVEMENTFLAG_FORWARD | MOVEMENTFLAG_BACKWARD);
MOVEMENTFLAG_FORWARD | MOVEMENTFLAG_BACKWARD);
//! Cannot walk on water without SPELL_AURA_WATER_WALK
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_WATERWALKING) &&
!GetPlayer()->HasAuraType(SPELL_AURA_WATER_WALK) &&
!GetPlayer()->HasAuraType(SPELL_AURA_GHOST),
MOVEMENTFLAG_WATERWALKING);
!GetPlayer()->HasAuraType(SPELL_AURA_WATER_WALK) &&
!GetPlayer()->HasAuraType(SPELL_AURA_GHOST),
MOVEMENTFLAG_WATERWALKING);
//! Cannot feather fall without SPELL_AURA_FEATHER_FALL
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_FALLING_SLOW) && !GetPlayer()->HasAuraType(SPELL_AURA_FEATHER_FALL),
MOVEMENTFLAG_FALLING_SLOW);
MOVEMENTFLAG_FALLING_SLOW);
/*! Cannot fly if no fly auras present. Exception is being a GM.
Note that we check for account level instead of Player::IsGameMaster() because in some
@@ -990,14 +990,14 @@ void WorldSession::ReadMovementInfo(WorldPacket& data, MovementInfo* mi)
*/
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_FLYING | MOVEMENTFLAG_CAN_FLY) && GetSecurity() == SEC_PLAYER && !GetPlayer()->m_mover->HasAuraType(SPELL_AURA_FLY) && !GetPlayer()->m_mover->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED),
MOVEMENTFLAG_FLYING | MOVEMENTFLAG_CAN_FLY);
MOVEMENTFLAG_FLYING | MOVEMENTFLAG_CAN_FLY);
//! Cannot fly and fall at the same time
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_DISABLE_GRAVITY) && mi->HasMovementFlag(MOVEMENTFLAG_FALLING),
MOVEMENTFLAG_FALLING);
MOVEMENTFLAG_FALLING);
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_SPLINE_ENABLED) &&
(!GetPlayer()->movespline->Initialized() || GetPlayer()->movespline->Finalized()), MOVEMENTFLAG_SPLINE_ENABLED);
(!GetPlayer()->movespline->Initialized() || GetPlayer()->movespline->Finalized()), MOVEMENTFLAG_SPLINE_ENABLED);
#undef REMOVE_VIOLATING_FLAGS
}
@@ -1040,7 +1040,7 @@ void WorldSession::WriteMovementInfo(WorldPacket* data, MovementInfo* mi)
*data << mi->splineElevation;
}
void WorldSession::ReadAddonsInfo(WorldPacket& data)
void WorldSession::ReadAddonsInfo(ByteBuffer& data)
{
if (data.rpos() + 4 > data.size())
return;
@@ -1218,7 +1218,7 @@ void WorldSession::InitWarden(SessionKey const& k, std::string const& os)
{
if (os == "Win")
{
_warden = new WardenWin();
_warden = std::make_unique<WardenWin>();
_warden->Init(this, k);
}
else if (os == "OSX")
@@ -1533,6 +1533,9 @@ uint32 WorldSession::DosProtection::GetMaxPacketCounterAllowed(uint16 opcode) co
return maxPacketCounterAllowed;
}
WorldSession::DosProtection::DosProtection(WorldSession* s) :
Session(s), _policy((Policy)sWorld->getIntConfig(CONFIG_PACKET_SPOOF_POLICY)) { }
void WorldSession::ResetTimeSync()
{
_timeSyncNextCounter = 0;

View File

@@ -230,14 +230,14 @@ struct PacketCounter
class WorldSession
{
public:
WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter, bool skipQueue, uint32 TotalTime);
WorldSession(uint32 id, std::string&& name, std::shared_ptr<WorldSocket> sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter, bool skipQueue, uint32 TotalTime);
~WorldSession();
bool PlayerLoading() const { return m_playerLoading; }
bool PlayerLogout() const { return m_playerLogout; }
bool PlayerLogoutWithSave() const { return m_playerLogout && m_playerSave; }
void ReadAddonsInfo(WorldPacket& data);
void ReadAddonsInfo(ByteBuffer& data);
void SendAddonsInfo();
void ReadMovementInfo(WorldPacket& data, MovementInfo* mi);
@@ -987,7 +987,7 @@ protected:
{
friend class World;
public:
DosProtection(WorldSession* s) : Session(s), _policy((Policy)sWorld->getIntConfig(CONFIG_PACKET_SPOOF_POLICY)) { }
DosProtection(WorldSession* s);
bool EvaluateOpcode(WorldPacket& p, time_t time) const;
protected:
enum Policy
@@ -1035,20 +1035,20 @@ private:
ObjectGuid::LowType m_GUIDLow;
Player* _player;
WorldSocket* m_Socket;
std::shared_ptr<WorldSocket> m_Socket;
std::string m_Address;
// std::string m_LAddress; // Last Attempted Remote Adress - we can not set attempted ip for a non-existing session!
AccountTypes _security;
bool _skipQueue;
uint32 _accountId;
std::string _accountName;
uint8 m_expansion;
uint32 m_total_time;
typedef std::list<AddonInfo> AddonsList;
// Warden
Warden* _warden; // Remains nullptr if Warden system is not enabled by config
std::unique_ptr<Warden> _warden; // Remains nullptr if Warden system is not enabled by config
time_t _logoutTime;
bool m_inQueue; // session wait in auth.queue
@@ -1057,7 +1057,7 @@ private:
bool m_playerSave;
LocaleConstant m_sessionDbcLocale;
LocaleConstant m_sessionDbLocaleIndex;
uint32 m_latency;
std::atomic<uint32> m_latency;
AccountData m_accountData[NUM_ACCOUNT_DATA_TYPES];
uint32 m_Tutorials[MAX_ACCOUNT_TUTORIAL_VALUES];
bool m_TutorialsChanged;
@@ -1081,6 +1081,9 @@ private:
std::map<uint32, uint32> _pendingTimeSyncRequests; // key: counter. value: server time when packet with that counter was sent.
uint32 _timeSyncNextCounter;
uint32 _timeSyncTimer;
WorldSession(WorldSession const& right) = delete;
WorldSession& operator=(WorldSession const& right) = delete;
};
#endif
/// @}

File diff suppressed because it is too large Load Diff

View File

@@ -4,197 +4,121 @@
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*/
/** \addtogroup u2w User to World Communication
* @{
* \file WorldSocket.h
* \author Derex <derex101@gmail.com>
*/
#ifndef __WORLDSOCKET_H__
#define __WORLDSOCKET_H__
#ifndef _WORLDSOCKET_H
#define _WORLDSOCKET_H
#include "AuthCrypt.h"
#include "Common.h"
#include "Duration.h"
#include <ace/Message_Block.h>
#include <ace/SOCK_Stream.h>
#include <ace/Svc_Handler.h>
#include <ace/Synch_Traits.h>
#include <ace/Unbounded_Queue.h>
#include <mutex>
#include "AuthCrypt.h"
#include "ServerPktHeader.h"
#include "Socket.h"
#include "Util.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "MPSCQueue.h"
#include <boost/asio/ip/tcp.hpp>
#if !defined (ACE_LACKS_PRAGMA_ONCE)
#pragma once
#endif /* ACE_LACKS_PRAGMA_ONCE */
using boost::asio::ip::tcp;
class ACE_Message_Block;
class WorldPacket;
class WorldSession;
class EncryptablePacket : public WorldPacket
{
public:
EncryptablePacket(WorldPacket const& packet, bool encrypt) : WorldPacket(packet), _encrypt(encrypt)
{
SocketQueueLink.store(nullptr, std::memory_order_relaxed);
}
bool NeedsEncryption() const { return _encrypt; }
std::atomic<EncryptablePacket*> SocketQueueLink;
private:
bool _encrypt;
};
namespace WorldPackets
{
class ServerPacket;
}
/// 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 4096 bytes buffer on stack
* to which it does recv() calls. And then received data is
* distributed where its needed. 4096 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 : public WorldHandler
#pragma pack(push, 1)
struct ClientPktHeader
{
uint16 size;
uint32 cmd;
bool IsValidSize() const { return size >= 4 && size < 10240; }
bool IsValidOpcode() const { return cmd < NUM_OPCODE_HANDLERS; }
};
#pragma pack(pop)
struct AuthSession;
class AC_GAME_API WorldSocket : public Socket<WorldSocket>
{
typedef Socket<WorldSocket> BaseSocket;
public:
WorldSocket (void);
virtual ~WorldSocket (void);
WorldSocket(tcp::socket&& socket);
~WorldSocket();
friend class WorldSocketMgr;
WorldSocket(WorldSocket const& right) = delete;
WorldSocket& operator=(WorldSocket const& right) = delete;
/// Check if socket is closed.
bool IsClosed (void) const;
void Start() override;
bool Update() override;
/// Close the socket.
void CloseSocket(std::string const& reason);
void SendPacket(WorldPacket const& packet);
/// Get address of connected peer.
const std::string& GetRemoteAddress (void) const;
void SetSendBufferSize(std::size_t sendBufferSize) { _sendBufferSize = sendBufferSize; }
/// Send A packet on the socket, this function is reentrant.
/// @param pct packet to send
/// @return -1 of failure
int SendPacket(const WorldPacket& pct);
protected:
void OnClose() override;
void ReadHandler() override;
bool ReadHeaderHandler();
/// Add reference to this object.
long AddReference (void);
enum class ReadDataHandlerResult
{
Ok = 0,
Error = 1,
WaitingForQuery = 2
};
/// Remove reference to this object.
long RemoveReference (void);
/// things called by ACE framework.
/// 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 (u_long);
/// 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);
ReadDataHandlerResult ReadDataHandler();
private:
/// Helper functions for processing incoming data.
int handle_input_header (void);
int handle_input_payload (void);
int handle_input_missing_data (void);
void CheckIpCallback(PreparedQueryResult result);
/// 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();
int schedule_wakeup_output();
/// writes network.opcode log
/// accessing WorldSession is not threadsafe, only do it when holding _worldSessionLock
void LogOpcodeText(OpcodeClient opcode, std::unique_lock<std::mutex> const& guard) const;
/// Drain the queue if its not empty.
int handle_output_queue();
/// sends and logs network.opcode without accessing WorldSession
void SendPacketAndLogOpcode(WorldPacket const& packet);
void HandleSendAuthSession();
void HandleAuthSession(WorldPacket& recvPacket);
void HandleAuthSessionCallback(std::shared_ptr<AuthSession> authSession, PreparedQueryResult result);
void LoadSessionPermissionsCallback(PreparedQueryResult result);
void SendAuthResponseError(uint8 code);
/// process one incoming packet.
/// @param new_pct received packet, note that you need to delete it.
int ProcessIncoming (WorldPacket* new_pct);
bool HandlePing(WorldPacket& recvPacket);
/// Called by ProcessIncoming() on CMSG_AUTH_SESSION.
int HandleAuthSession (WorldPacket& recvPacket);
std::array<uint8, 4> _authSeed;
AuthCrypt _authCrypt;
/// Called by ProcessIncoming() on CMSG_PING.
int HandlePing (WorldPacket& recvPacket);
TimePoint _LastPingTime;
uint32 _OverSpeedPings;
private:
/// Time in which the last ping was received
SystemTimePoint m_LastPingTime;
std::mutex _worldSessionLock;
WorldSession* _worldSession;
bool _authed;
/// 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
std::mutex 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.
std::mutex 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;
std::array<uint8, 4> m_Seed;
MessageBuffer _headerBuffer;
MessageBuffer _packetBuffer;
MPSCQueue<EncryptablePacket, &EncryptablePacket::SocketQueueLink> _bufferQueue;
std::size_t _sendBufferSize;
QueryCallbackProcessor _queryProcessor;
std::string _ipCountry;
};
#endif /* _WORLDSOCKET_H */
/// @}
#endif

View File

@@ -1,53 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*/
/** \addtogroup u2w User to World Communication
* @{
* \file WorldSocketMgr.h
*/
#ifndef __WORLDSOCKETACCEPTOR_H_
#define __WORLDSOCKETACCEPTOR_H_
#include "Common.h"
#include "WorldSocket.h"
#include <ace/Acceptor.h>
#include <ace/SOCK_Acceptor.h>
class WorldSocketAcceptor : public ACE_Acceptor<WorldSocket, ACE_SOCK_Acceptor>
{
public:
WorldSocketAcceptor(void) { }
virtual ~WorldSocketAcceptor(void)
{
if (reactor())
reactor()->cancel_timer(this, 1);
}
protected:
virtual int handle_timeout(const ACE_Time_Value& /*current_time*/, const void* /*act = 0*/)
{
LOG_INFO("network", "Resuming acceptor");
reactor()->cancel_timer(this, 1);
return reactor()->register_handler(this, ACE_Event_Handler::ACCEPT_MASK);
}
virtual int handle_accept_error(void)
{
#if defined(ENFILE) && defined(EMFILE)
if (errno == ENFILE || errno == EMFILE)
{
LOG_ERROR("network", "Out of file descriptors, suspending incoming connections for 10 seconds");
reactor()->remove_handler(this, ACE_Event_Handler::ACCEPT_MASK | ACE_Event_Handler::DONT_CALL);
reactor()->schedule_timer(this, nullptr, ACE_Time_Value(10));
}
#endif
return 0;
}
};
#endif /* __WORLDSOCKETACCEPTOR_H_ */
/// @}

View File

@@ -4,345 +4,109 @@
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*/
/** \file WorldSocketMgr.cpp
* \ingroup u2w
* \author Derex <derex101@gmail.com>
*/
#include "Config.h"
#include "DatabaseEnv.h"
#include "Log.h"
#include "NetworkThread.h"
#include "ScriptMgr.h"
#include "WorldSocket.h"
#include "WorldSocketAcceptor.h"
#include "WorldSocketMgr.h"
#include <ace/ACE.h>
#include <ace/Dev_Poll_Reactor.h>
#include <ace/Log_Msg.h>
#include <ace/os_include/arpa/os_inet.h>
#include <ace/os_include/netinet/os_tcp.h>
#include <ace/os_include/sys/os_socket.h>
#include <ace/Reactor_Impl.h>
#include <ace/Reactor.h>
#include <ace/TP_Reactor.h>
#include <atomic>
#include <set>
#include <boost/system/error_code.hpp>
/**
* 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
static void OnSocketAccept(tcp::socket&& sock, uint32 threadIndex)
{
sWorldSocketMgr.OnSocketOpen(std::forward<tcp::socket>(sock), threadIndex);
}
class WorldSocketThread : public NetworkThread<WorldSocket>
{
public:
ReactorRunnable() :
m_Reactor(0),
m_Connections(0),
m_ThreadId(-1)
void SocketAdded(std::shared_ptr<WorldSocket> sock) override
{
ACE_Reactor_Impl* imp;
#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);
}
~ReactorRunnable() override
{
Stop();
Wait();
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 m_Connections;
}
int AddSocket (WorldSocket* sock)
{
std::lock_guard<std::mutex> guard(m_NewSockets_Lock);
++m_Connections;
sock->AddReference();
sock->reactor (m_Reactor);
m_NewSockets.insert (sock);
sock->SetSendBufferSize(sWorldSocketMgr.GetApplicationSendBufferSize());
sScriptMgr->OnSocketOpen(sock);
return 0;
}
ACE_Reactor* GetReactor()
void SocketRemoved(std::shared_ptr<WorldSocket> sock) override
{
return m_Reactor;
sScriptMgr->OnSocketClose(sock);
}
protected:
void AddNewSockets()
{
std::lock_guard<std::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())
{
sScriptMgr->OnSocketClose(sock, true);
sock->RemoveReference();
--m_Connections;
}
else
m_Sockets.insert (sock);
}
m_NewSockets.clear();
}
int svc() override
{
LOG_DEBUG("network", "Network Thread Starting");
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("svc()");
sScriptMgr->OnSocketClose((*t), false);
(*t)->RemoveReference();
--m_Connections;
m_Sockets.erase (t);
}
else
++i;
}
}
LOG_DEBUG("network", "Network Thread exits");
return 0;
}
private:
typedef std::atomic<int> AtomicInt;
typedef std::set<WorldSocket*> SocketSet;
ACE_Reactor* m_Reactor;
AtomicInt m_Connections;
int m_ThreadId;
SocketSet m_Sockets;
SocketSet m_NewSockets;
std::mutex m_NewSockets_Lock;
};
WorldSocketMgr::WorldSocketMgr() :
m_NetThreads(0),
m_NetThreadsCount(0),
m_SockOutKBuff(-1),
m_SockOutUBuff(65536),
m_UseNoDelay(true),
m_Acceptor (0)
BaseSocketMgr(), _socketSystemSendBufferSize(-1), _socketApplicationSendBufferSize(65536), _tcpNoDelay(true)
{
}
WorldSocketMgr::~WorldSocketMgr()
{
delete [] m_NetThreads;
delete m_Acceptor;
}
WorldSocketMgr* WorldSocketMgr::instance()
WorldSocketMgr& WorldSocketMgr::Instance()
{
static WorldSocketMgr instance;
return &instance;
return instance;
}
int
WorldSocketMgr::StartReactiveIO (uint16 port, const char* address)
bool WorldSocketMgr::StartWorldNetwork(Acore::Asio::IoContext& ioContext, std::string const& bindIp, uint16 port, int threadCount)
{
m_UseNoDelay = sConfigMgr->GetOption<bool> ("Network.TcpNodelay", true);
_tcpNoDelay = sConfigMgr->GetOption<bool>("Network.TcpNodelay", true);
int num_threads = sConfigMgr->GetOption<int32> ("Network.Threads", 1);
if (num_threads <= 0)
{
LOG_ERROR("network", "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];
LOG_INFO("network", "Max allowed socket connections %d", ACE::max_handles());
int const max_connections = ACORE_MAX_LISTEN_CONNECTIONS;
LOG_DEBUG("network", "Max allowed socket connections %d", max_connections);
// -1 means use default
m_SockOutKBuff = sConfigMgr->GetOption<int32> ("Network.OutKBuff", -1);
_socketSystemSendBufferSize = sConfigMgr->GetOption<int32>("Network.OutKBuff", -1);
_socketApplicationSendBufferSize = sConfigMgr->GetOption<int32>("Network.OutUBuff", 65536);
m_SockOutUBuff = sConfigMgr->GetOption<int32> ("Network.OutUBuff", 65536);
if (m_SockOutUBuff <= 0)
if (_socketApplicationSendBufferSize <= 0)
{
LOG_ERROR("network", "Network.OutUBuff is wrong in your config file");
return -1;
return false;
}
m_Acceptor = new WorldSocketAcceptor;
if (!BaseSocketMgr::StartNetwork(ioContext, bindIp, port, threadCount))
return false;
ACE_INET_Addr listen_addr (port, address);
if (m_Acceptor->open(listen_addr, m_NetThreads[0].GetReactor(), ACE_NONBLOCK) == -1)
{
LOG_ERROR("network", "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 (uint16 port, const char* address)
{
if (!sLog->ShouldLog("network", LogLevel::LOG_LEVEL_DEBUG))
ACE_Log_Msg::instance()->priority_mask(LM_ERROR, ACE_Log_Msg::PROCESS);
if (StartReactiveIO(port, address) == -1)
return -1;
_acceptor->AsyncAcceptWithCallback<&OnSocketAccept>();
sScriptMgr->OnNetworkStart();
return 0;
return true;
}
void
WorldSocketMgr::StopNetwork()
void WorldSocketMgr::StopNetwork()
{
if (m_Acceptor)
{
m_Acceptor->close();
}
if (m_NetThreadsCount != 0)
{
for (size_t i = 0; i < m_NetThreadsCount; ++i)
m_NetThreads[i].Stop();
}
Wait();
BaseSocketMgr::StopNetwork();
sScriptMgr->OnNetworkStop();
}
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)
void WorldSocketMgr::OnSocketOpen(tcp::socket&& sock, uint32 threadIndex)
{
// set some options here
if (m_SockOutKBuff >= 0)
if (_socketSystemSendBufferSize >= 0)
{
if (sock->peer().set_option (SOL_SOCKET,
SO_SNDBUF,
(void*) & m_SockOutKBuff,
sizeof (int)) == -1 && errno != ENOTSUP)
boost::system::error_code err;
sock.set_option(boost::asio::socket_base::send_buffer_size(_socketSystemSendBufferSize), err);
if (err && err != boost::system::errc::not_supported)
{
LOG_ERROR("network", "WorldSocketMgr::OnSocketOpen set_option SO_SNDBUF");
return -1;
LOG_ERROR("network", "WorldSocketMgr::OnSocketOpen sock.set_option(boost::asio::socket_base::send_buffer_size) err = %s", err.message().c_str());
return;
}
}
static const int ndoption = 1;
// Set TCP_NODELAY.
if (m_UseNoDelay)
if (_tcpNoDelay)
{
if (sock->peer().set_option (ACE_IPPROTO_TCP,
TCP_NODELAY,
(void*)&ndoption,
sizeof (int)) == -1)
boost::system::error_code err;
sock.set_option(boost::asio::ip::tcp::no_delay(true), err);
if (err)
{
LOG_ERROR("network", "WorldSocketMgr::OnSocketOpen: peer().set_option TCP_NODELAY errno = %s", ACE_OS::strerror (errno));
return -1;
LOG_ERROR("network", "WorldSocketMgr::OnSocketOpen sock.set_option(boost::asio::ip::tcp::no_delay) err = %s", err.message().c_str());
return;
}
}
sock->m_OutBufferSize = static_cast<size_t> (m_SockOutUBuff);
// we skip the Acceptor Thread
size_t min = 1;
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);
BaseSocketMgr::OnSocketOpen(std::forward<tcp::socket>(sock), threadIndex);
}
NetworkThread<WorldSocket>* WorldSocketMgr::CreateThreads() const
{
return new WorldSocketThread[GetNetworkThreadCount()];
}

View File

@@ -4,58 +4,43 @@
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*/
/** \addtogroup u2w User to World Communication
* @{
* \file WorldSocketMgr.h
* \author Derex <derex101@gmail.com>
*/
#ifndef __WORLDSOCKETMGR_H
#define __WORLDSOCKETMGR_H
#include "Common.h"
#include "SocketMgr.h"
class WorldSocket;
class ReactorRunnable;
class ACE_Event_Handler;
/// Manages all sockets connected to peers and network threads
class WorldSocketMgr
class AC_GAME_API WorldSocketMgr : public SocketMgr<WorldSocket>
{
public:
friend class WorldSocket;
typedef SocketMgr<WorldSocket> BaseSocketMgr;
static WorldSocketMgr* instance();
public:
static WorldSocketMgr& Instance();
/// Start network, listen at address:port .
int StartNetwork(uint16 port, const char* address);
bool StartWorldNetwork(Acore::Asio::IoContext& ioContext, std::string const& bindIp, uint16 port, int networkThreads);
/// Stops all network threads, It will wait for all running threads .
void StopNetwork();
void StopNetwork() override;
/// Wait untill all network threads have "joined" .
void Wait();
void OnSocketOpen(tcp::socket&& sock, uint32 threadIndex) override;
private:
int OnSocketOpen(WorldSocket* sock);
std::size_t GetApplicationSendBufferSize() const { return _socketApplicationSendBufferSize; }
int StartReactiveIO(uint16 port, const char* address);
private:
protected:
WorldSocketMgr();
virtual ~WorldSocketMgr();
ReactorRunnable* m_NetThreads;
size_t m_NetThreadsCount;
NetworkThread<WorldSocket>* CreateThreads() const override;
int m_SockOutKBuff;
int m_SockOutUBuff;
bool m_UseNoDelay;
class WorldSocketAcceptor* m_Acceptor;
private:
int32 _socketSystemSendBufferSize;
int32 _socketApplicationSendBufferSize;
bool _tcpNoDelay;
};
#define sWorldSocketMgr WorldSocketMgr::instance()
#define sWorldSocketMgr WorldSocketMgr::Instance()
#endif
/// @}

View File

@@ -8,6 +8,7 @@
\ingroup world
*/
#include "World.h"
#include "AccountMgr.h"
#include "AchievementMgr.h"
#include "AddonMgr.h"
@@ -28,8 +29,8 @@
#include "CreatureAIRegistry.h"
#include "CreatureGroups.h"
#include "CreatureTextMgr.h"
#include "DatabaseEnv.h"
#include "DBCStores.h"
#include "DatabaseEnv.h"
#include "DisableMgr.h"
#include "DynamicVisibility.h"
#include "GameEventMgr.h"
@@ -38,16 +39,16 @@
#include "GridNotifiersImpl.h"
#include "GroupMgr.h"
#include "GuildMgr.h"
#include "IPLocation.h"
#include "InstanceSaveMgr.h"
#include "ItemEnchantmentMgr.h"
#include "IPLocation.h"
#include "Language.h"
#include "LFGMgr.h"
#include "Language.h"
#include "Log.h"
#include "LootItemStorage.h"
#include "LootMgr.h"
#include "MapManager.h"
#include "MMapFactory.h"
#include "MapManager.h"
#include "ObjectMgr.h"
#include "Opcodes.h"
#include "OutdoorPvPMgr.h"
@@ -66,17 +67,18 @@
#include "Transport.h"
#include "TransportMgr.h"
#include "Util.h"
#include "Vehicle.h"
#include "VMapFactory.h"
#include "VMapManager2.h"
#include "Vehicle.h"
#include "Warden.h"
#include "WardenCheckMgr.h"
#include "WaypointMovementGenerator.h"
#include "WeatherMgr.h"
#include "WhoListCache.h"
#include "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include <VMapManager2.h>
#include <boost/asio/ip/address.hpp>
#include <cmath>
#ifdef ELUNA
#include "LuaEngine.h"