Initial commit
This commit is contained in:
330
src/server/game/Misc/BanMgr.cpp
Normal file
330
src/server/game/Misc/BanMgr.cpp
Normal file
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "BanMgr.h"
|
||||
#include "AccountMgr.h"
|
||||
#include "Chat.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "GameTime.h"
|
||||
#include "Language.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "Player.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "World.h"
|
||||
#include "WorldSession.h"
|
||||
#include "WorldSessionMgr.h"
|
||||
|
||||
BanMgr* BanMgr::instance()
|
||||
{
|
||||
static BanMgr instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
/// Ban an account, duration will be parsed using TimeStringToSecs if it is positive, otherwise permban
|
||||
BanReturn BanMgr::BanAccount(std::string const& AccountName, std::string const& Duration, std::string const& Reason, std::string const& Author)
|
||||
{
|
||||
if (AccountName.empty() || Duration.empty())
|
||||
return BAN_SYNTAX_ERROR;
|
||||
|
||||
uint32 DurationSecs = TimeStringToSecs(Duration);
|
||||
|
||||
uint32 AccountID = AccountMgr::GetId(AccountName);
|
||||
if (!AccountID)
|
||||
return BAN_NOTFOUND;
|
||||
|
||||
///- Disconnect all affected players (for IP it can be several)
|
||||
LoginDatabaseTransaction trans = LoginDatabase.BeginTransaction();
|
||||
|
||||
// pussywizard: check existing ban to prevent overriding by a shorter one! >_>
|
||||
LoginDatabasePreparedStatement* stmtAccountBanned = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BANNED);
|
||||
stmtAccountBanned->SetData(0, AccountID);
|
||||
|
||||
PreparedQueryResult banresult = LoginDatabase.Query(stmtAccountBanned);
|
||||
if (banresult && ((*banresult)[0].Get<uint32>() == (*banresult)[1].Get<uint32>() || ((*banresult)[1].Get<uint32>() > GameTime::GetGameTime().count() + DurationSecs && DurationSecs)))
|
||||
return BAN_LONGER_EXISTS;
|
||||
|
||||
// make sure there is only one active ban
|
||||
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_ACCOUNT_NOT_BANNED);
|
||||
stmt->SetData(0, AccountID);
|
||||
trans->Append(stmt);
|
||||
|
||||
// No SQL injection with prepared statements
|
||||
stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ACCOUNT_BANNED);
|
||||
stmt->SetData(0, AccountID);
|
||||
stmt->SetData(1, DurationSecs);
|
||||
stmt->SetData(2, Author);
|
||||
stmt->SetData(3, Reason);
|
||||
trans->Append(stmt);
|
||||
|
||||
if (WorldSession* session = sWorldSessionMgr->FindSession(AccountID))
|
||||
if (session->GetPlayerName() != Author)
|
||||
session->KickPlayer("Ban Account at condition 'FindSession(account)->GetPlayerName() != author'");
|
||||
|
||||
if (WorldSession* session = sWorldSessionMgr->FindOfflineSession(AccountID))
|
||||
if (session->GetPlayerName() != Author)
|
||||
session->KickPlayer("Ban Account at condition 'FindOfflineSession(account)->GetPlayerName() != author'");
|
||||
|
||||
LoginDatabase.CommitTransaction(trans);
|
||||
|
||||
if (sWorld->getBoolConfig(CONFIG_SHOW_BAN_IN_WORLD))
|
||||
{
|
||||
bool IsPermanetly = true;
|
||||
|
||||
if (TimeStringToSecs(Duration) > 0)
|
||||
IsPermanetly = false;
|
||||
|
||||
if (!IsPermanetly)
|
||||
ChatHandler(nullptr).SendWorldText(LANG_BAN_ACCOUNT_YOUBANNEDMESSAGE_WORLD, Author, AccountName, secsToTimeString(TimeStringToSecs(Duration), true), Reason);
|
||||
else
|
||||
ChatHandler(nullptr).SendWorldText(LANG_BAN_ACCOUNT_YOUPERMBANNEDMESSAGE_WORLD, Author, AccountName, Reason);
|
||||
}
|
||||
|
||||
return BAN_SUCCESS;
|
||||
}
|
||||
|
||||
/// Ban an account by player name, duration will be parsed using TimeStringToSecs if it is positive, otherwise permban
|
||||
BanReturn BanMgr::BanAccountByPlayerName(std::string const& CharacterName, std::string const& Duration, std::string const& Reason, std::string const& Author)
|
||||
{
|
||||
if (CharacterName.empty() || Duration.empty())
|
||||
return BAN_SYNTAX_ERROR;
|
||||
|
||||
uint32 DurationSecs = TimeStringToSecs(Duration);
|
||||
|
||||
uint32 AccountID = sCharacterCache->GetCharacterAccountIdByName(CharacterName);
|
||||
if (!AccountID)
|
||||
return BAN_NOTFOUND;
|
||||
|
||||
///- Disconnect all affected players (for IP it can be several)
|
||||
LoginDatabaseTransaction trans = LoginDatabase.BeginTransaction();
|
||||
|
||||
// pussywizard: check existing ban to prevent overriding by a shorter one! >_>
|
||||
LoginDatabasePreparedStatement* stmtAccountBanned = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BANNED);
|
||||
stmtAccountBanned->SetData(0, AccountID);
|
||||
|
||||
PreparedQueryResult banresult = LoginDatabase.Query(stmtAccountBanned);
|
||||
if (banresult && ((*banresult)[0].Get<uint32>() == (*banresult)[1].Get<uint32>() || ((*banresult)[1].Get<uint32>() > GameTime::GetGameTime().count() + DurationSecs && DurationSecs)))
|
||||
return BAN_LONGER_EXISTS;
|
||||
|
||||
// make sure there is only one active ban
|
||||
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_ACCOUNT_NOT_BANNED);
|
||||
stmt->SetData(0, AccountID);
|
||||
trans->Append(stmt);
|
||||
|
||||
// No SQL injection with prepared statements
|
||||
stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ACCOUNT_BANNED);
|
||||
stmt->SetData(0, AccountID);
|
||||
stmt->SetData(1, DurationSecs);
|
||||
stmt->SetData(2, Author);
|
||||
stmt->SetData(3, Reason);
|
||||
trans->Append(stmt);
|
||||
|
||||
if (WorldSession* session = sWorldSessionMgr->FindSession(AccountID))
|
||||
if (session->GetPlayerName() != Author)
|
||||
session->KickPlayer("Ban Account at condition 'FindSession(account)->GetPlayerName() != author'");
|
||||
|
||||
if (WorldSession* session = sWorldSessionMgr->FindOfflineSession(AccountID))
|
||||
if (session->GetPlayerName() != Author)
|
||||
session->KickPlayer("Ban Account at condition 'FindOfflineSession(account)->GetPlayerName() != author'");
|
||||
|
||||
LoginDatabase.CommitTransaction(trans);
|
||||
|
||||
if (sWorld->getBoolConfig(CONFIG_SHOW_BAN_IN_WORLD))
|
||||
{
|
||||
bool IsPermanetly = true;
|
||||
|
||||
if (TimeStringToSecs(Duration) > 0)
|
||||
IsPermanetly = false;
|
||||
|
||||
std::string AccountName;
|
||||
|
||||
AccountMgr::GetName(AccountID, AccountName);
|
||||
|
||||
if (!IsPermanetly)
|
||||
ChatHandler(nullptr).SendWorldText(LANG_BAN_ACCOUNT_YOUBANNEDMESSAGE_WORLD, Author, AccountName, secsToTimeString(TimeStringToSecs(Duration), true), Reason);
|
||||
else
|
||||
ChatHandler(nullptr).SendWorldText(LANG_BAN_ACCOUNT_YOUPERMBANNEDMESSAGE_WORLD, Author, AccountName, Reason);
|
||||
}
|
||||
|
||||
return BAN_SUCCESS;
|
||||
}
|
||||
|
||||
/// Ban an IP address, duration will be parsed using TimeStringToSecs if it is positive, otherwise permban
|
||||
BanReturn BanMgr::BanIP(std::string const& IP, std::string const& Duration, std::string const& Reason, std::string const& Author)
|
||||
{
|
||||
if (IP.empty() || Duration.empty())
|
||||
return BAN_SYNTAX_ERROR;
|
||||
|
||||
uint32 DurationSecs = TimeStringToSecs(Duration);
|
||||
|
||||
// No SQL injection with prepared statements
|
||||
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BY_IP);
|
||||
stmt->SetData(0, IP);
|
||||
PreparedQueryResult resultAccounts = LoginDatabase.Query(stmt);
|
||||
|
||||
stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_IP_BANNED);
|
||||
stmt->SetData(0, IP);
|
||||
stmt->SetData(1, DurationSecs);
|
||||
stmt->SetData(2, Author);
|
||||
stmt->SetData(3, Reason);
|
||||
LoginDatabase.Execute(stmt);
|
||||
|
||||
if (sWorld->getBoolConfig(CONFIG_SHOW_BAN_IN_WORLD))
|
||||
{
|
||||
bool IsPermanetly = true;
|
||||
|
||||
if (TimeStringToSecs(Duration) > 0)
|
||||
IsPermanetly = false;
|
||||
|
||||
if (IsPermanetly)
|
||||
ChatHandler(nullptr).SendWorldText(LANG_BAN_IP_YOUPERMBANNEDMESSAGE_WORLD, Author, IP, Reason);
|
||||
else
|
||||
ChatHandler(nullptr).SendWorldText(LANG_BAN_IP_YOUBANNEDMESSAGE_WORLD, Author, IP, secsToTimeString(TimeStringToSecs(Duration), true), Reason);
|
||||
}
|
||||
|
||||
if (!resultAccounts)
|
||||
return BAN_SUCCESS;
|
||||
|
||||
///- Disconnect all affected players (for IP it can be several)
|
||||
LoginDatabaseTransaction trans = LoginDatabase.BeginTransaction();
|
||||
|
||||
do
|
||||
{
|
||||
Field* fields = resultAccounts->Fetch();
|
||||
uint32 AccountID = fields[0].Get<uint32>();
|
||||
|
||||
if (WorldSession* session = sWorldSessionMgr->FindSession(AccountID))
|
||||
if (session->GetPlayerName() != Author)
|
||||
session->KickPlayer("Ban IP at condition 'FindSession(account)->GetPlayerName() != author'");
|
||||
|
||||
if (WorldSession* session = sWorldSessionMgr->FindOfflineSession(AccountID))
|
||||
if (session->GetPlayerName() != Author)
|
||||
session->KickPlayer("Ban IP at condition 'FindOfflineSession(account)->GetPlayerName() != author'");
|
||||
} while (resultAccounts->NextRow());
|
||||
|
||||
LoginDatabase.CommitTransaction(trans);
|
||||
|
||||
return BAN_SUCCESS;
|
||||
}
|
||||
|
||||
/// Ban an character, duration will be parsed using TimeStringToSecs if it is positive, otherwise permban
|
||||
BanReturn BanMgr::BanCharacter(std::string const& CharacterName, std::string const& Duration, std::string const& Reason, std::string const& Author)
|
||||
{
|
||||
Player* target = ObjectAccessor::FindPlayerByName(CharacterName, false);
|
||||
uint32 DurationSecs = TimeStringToSecs(Duration);
|
||||
ObjectGuid TargetGUID;
|
||||
|
||||
/// Pick a player to ban if not online
|
||||
if (!target)
|
||||
{
|
||||
TargetGUID = sCharacterCache->GetCharacterGuidByName(CharacterName);
|
||||
if (!TargetGUID)
|
||||
return BAN_NOTFOUND;
|
||||
}
|
||||
else
|
||||
TargetGUID = target->GetGUID();
|
||||
|
||||
// make sure there is only one active ban
|
||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHARACTER_BAN);
|
||||
stmt->SetData(0, TargetGUID.GetCounter());
|
||||
CharacterDatabase.Execute(stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_BAN);
|
||||
stmt->SetData(0, TargetGUID.GetCounter());
|
||||
stmt->SetData(1, DurationSecs);
|
||||
stmt->SetData(2, Author);
|
||||
stmt->SetData(3, Reason);
|
||||
CharacterDatabase.Execute(stmt);
|
||||
|
||||
if (target)
|
||||
target->GetSession()->KickPlayer("Ban");
|
||||
|
||||
if (sWorld->getBoolConfig(CONFIG_SHOW_BAN_IN_WORLD))
|
||||
{
|
||||
bool IsPermanetly = true;
|
||||
|
||||
if (TimeStringToSecs(Duration) > 0)
|
||||
IsPermanetly = false;
|
||||
|
||||
if (!IsPermanetly)
|
||||
ChatHandler(nullptr).SendWorldText(LANG_BAN_CHARACTER_YOUBANNEDMESSAGE_WORLD, Author, CharacterName, secsToTimeString(TimeStringToSecs(Duration), true), Reason);
|
||||
else
|
||||
ChatHandler(nullptr).SendWorldText(LANG_BAN_CHARACTER_YOUPERMBANNEDMESSAGE_WORLD, Author, CharacterName, Reason);
|
||||
}
|
||||
|
||||
return BAN_SUCCESS;
|
||||
}
|
||||
|
||||
/// Remove a ban from an account
|
||||
bool BanMgr::RemoveBanAccount(std::string const& AccountName)
|
||||
{
|
||||
uint32 AccountID = AccountMgr::GetId(AccountName);
|
||||
if (!AccountID)
|
||||
return false;
|
||||
|
||||
// NO SQL injection as account is uint32
|
||||
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_ACCOUNT_NOT_BANNED);
|
||||
stmt->SetData(0, AccountID);
|
||||
LoginDatabase.Execute(stmt);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Remove a ban from an player name
|
||||
bool BanMgr::RemoveBanAccountByPlayerName(std::string const& CharacterName)
|
||||
{
|
||||
uint32 AccountID = sCharacterCache->GetCharacterAccountIdByName(CharacterName);
|
||||
if (!AccountID)
|
||||
return false;
|
||||
|
||||
// NO SQL injection as account is uint32
|
||||
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_ACCOUNT_NOT_BANNED);
|
||||
stmt->SetData(0, AccountID);
|
||||
LoginDatabase.Execute(stmt);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Remove a ban from an account
|
||||
bool BanMgr::RemoveBanIP(std::string const& IP)
|
||||
{
|
||||
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_IP_NOT_BANNED);
|
||||
stmt->SetData(0, IP);
|
||||
LoginDatabase.Execute(stmt);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Remove a ban from a character
|
||||
bool BanMgr::RemoveBanCharacter(std::string const& CharacterName)
|
||||
{
|
||||
Player* pBanned = ObjectAccessor::FindPlayerByName(CharacterName, false);
|
||||
ObjectGuid guid;
|
||||
|
||||
/// Pick a player to ban if not online
|
||||
if (!pBanned)
|
||||
guid = sCharacterCache->GetCharacterGuidByName(CharacterName);
|
||||
else
|
||||
guid = pBanned->GetGUID();
|
||||
|
||||
if (!guid)
|
||||
return false;
|
||||
|
||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHARACTER_BAN);
|
||||
stmt->SetData(0, guid.GetCounter());
|
||||
CharacterDatabase.Execute(stmt);
|
||||
return true;
|
||||
}
|
||||
50
src/server/game/Misc/BanMgr.h
Normal file
50
src/server/game/Misc/BanMgr.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _BAN_MANAGER_H
|
||||
#define _BAN_MANAGER_H
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
/// Ban function return codes
|
||||
enum BanReturn
|
||||
{
|
||||
BAN_SUCCESS,
|
||||
BAN_SYNTAX_ERROR,
|
||||
BAN_NOTFOUND,
|
||||
BAN_LONGER_EXISTS
|
||||
};
|
||||
|
||||
class BanMgr
|
||||
{
|
||||
public:
|
||||
static BanMgr* instance();
|
||||
|
||||
BanReturn BanAccount(std::string const& AccountName, std::string const& Duration, std::string const& Reason, std::string const& Author);
|
||||
BanReturn BanAccountByPlayerName(std::string const& CharacterName, std::string const& Duration, std::string const& Reason, std::string const& Author);
|
||||
BanReturn BanIP(std::string const& IP, std::string const& Duration, std::string const& Reason, std::string const& Author);
|
||||
BanReturn BanCharacter(std::string const& CharacterName, std::string const& Duration, std::string const& Reason, std::string const& Author);
|
||||
|
||||
bool RemoveBanAccount(std::string const& AccountName);
|
||||
bool RemoveBanAccountByPlayerName(std::string const& CharacterName);
|
||||
bool RemoveBanIP(std::string const& IP);
|
||||
bool RemoveBanCharacter(std::string const& CharacterName);
|
||||
};
|
||||
|
||||
#define sBan BanMgr::instance()
|
||||
|
||||
#endif // _BAN_MANAGER_H
|
||||
28
src/server/game/Misc/DynamicVisibility.cpp
Normal file
28
src/server/game/Misc/DynamicVisibility.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "DynamicVisibility.h"
|
||||
|
||||
uint8 DynamicVisibilityMgr::visibilitySettingsIndex = 0;
|
||||
|
||||
void DynamicVisibilityMgr::Update(uint32 sessionCount)
|
||||
{
|
||||
if (sessionCount >= (visibilitySettingsIndex + 1) * ((uint32)VISIBILITY_SETTINGS_PLAYER_INTERVAL) && visibilitySettingsIndex < VISIBILITY_SETTINGS_MAX_INTERVAL_NUM - 1)
|
||||
++visibilitySettingsIndex;
|
||||
else if (visibilitySettingsIndex && sessionCount < visibilitySettingsIndex * ((uint32)VISIBILITY_SETTINGS_PLAYER_INTERVAL) - 100)
|
||||
--visibilitySettingsIndex;
|
||||
}
|
||||
58
src/server/game/Misc/DynamicVisibility.h
Normal file
58
src/server/game/Misc/DynamicVisibility.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __DYNAMICVISIBILITY_H
|
||||
#define __DYNAMICVISIBILITY_H
|
||||
|
||||
#include "Define.h"
|
||||
|
||||
struct VisibilitySettingData
|
||||
{
|
||||
uint32 visibilityNotifyDelay;
|
||||
uint32 aiNotifyDelay;
|
||||
float requiredMoveDistanceSq;
|
||||
};
|
||||
|
||||
// pussywizard: dynamic visibility settings
|
||||
// 7 player intervals: 0-499, 500-999, 1000-1499, 1500-1999, 2000-2499, 2500-2999, 3000+
|
||||
// 5 map types: common, instance, raid, bg, arena
|
||||
// feel free to add more intervals, change existing ones or move to conf file :P
|
||||
#define VISIBILITY_SETTINGS_PLAYER_INTERVAL 500
|
||||
#define VISIBILITY_SETTINGS_MAX_INTERVAL_NUM 7
|
||||
const VisibilitySettingData VisibilitySettings[VISIBILITY_SETTINGS_MAX_INTERVAL_NUM][5] =
|
||||
{
|
||||
{ {300, 150, 1.0f}, {300, 150, 1.0f}, {300, 150, 1.0f}, {300, 150, 1.0f}, {300, 150, 1.0f} }, // 0-499
|
||||
{ {400, 200, 2.25f}, {400, 200, 2.25f}, {400, 200, 2.25f}, {300, 150, 1.0f}, {300, 150, 1.0f} }, // 500-999
|
||||
{ {500, 250, 4.0f}, {500, 250, 4.0f}, {500, 250, 4.0f}, {400, 200, 2.25f}, {300, 150, 1.0f} }, // 1000-1499
|
||||
{ {700, 350, 6.25f}, {700, 350, 6.25f}, {700, 350, 6.25f}, {600, 300, 6.25f}, {300, 200, 1.0f} }, // 1500-1999
|
||||
{ {1000, 500, 16.0f}, {1000, 500, 16.0f}, {1000, 500, 16.0f}, {1000, 500, 16.0f}, {300, 250, 1.0f} }, // 2000-2499
|
||||
{ {1000, 500, 16.0f}, {1000, 500, 16.0f}, {1000, 500, 16.0f}, {1000, 500, 16.0f}, {300, 350, 1.0f} }, // 2500-2999
|
||||
{ {1200, 550, 20.0f}, {1200, 550, 25.0f}, {1200, 550, 25.0f}, {1100, 550, 16.0f}, {300, 350, 1.0f} } // 3000+
|
||||
};
|
||||
|
||||
class DynamicVisibilityMgr
|
||||
{
|
||||
public:
|
||||
static void Update(uint32 sessionCount);
|
||||
static uint32 GetVisibilityNotifyDelay(uint32 map_type) { return VisibilitySettings[visibilitySettingsIndex][map_type].visibilityNotifyDelay; }
|
||||
static uint32 GetAINotifyDelay(uint32 map_type) { return VisibilitySettings[visibilitySettingsIndex][map_type].aiNotifyDelay; }
|
||||
static float GetReqMoveDistSq(uint32 map_type) { return VisibilitySettings[visibilitySettingsIndex][map_type].requiredMoveDistanceSq; }
|
||||
protected:
|
||||
static uint8 visibilitySettingsIndex;
|
||||
};
|
||||
|
||||
#endif
|
||||
440
src/server/game/Misc/GameGraveyard.cpp
Normal file
440
src/server/game/Misc/GameGraveyard.cpp
Normal file
@@ -0,0 +1,440 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "GameGraveyard.h"
|
||||
#include "DBCStores.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "Log.h"
|
||||
#include "MapMgr.h"
|
||||
#include "ScriptMgr.h"
|
||||
|
||||
Graveyard* Graveyard::instance()
|
||||
{
|
||||
static Graveyard instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
void Graveyard::LoadGraveyardFromDB()
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
|
||||
_graveyardStore.clear();
|
||||
|
||||
QueryResult result = WorldDatabase.Query("SELECT ID, Map, x, y, z, Comment FROM game_graveyard");
|
||||
if (!result)
|
||||
{
|
||||
LOG_WARN("server.loading", ">> Loaded 0 graveyard. Table `game_graveyard` is empty!");
|
||||
LOG_INFO("server.loading", " ");
|
||||
return;
|
||||
}
|
||||
|
||||
int32 Count = 0;
|
||||
|
||||
do
|
||||
{
|
||||
GraveyardStruct Graveyard;
|
||||
|
||||
Field* fields = result->Fetch();
|
||||
|
||||
Graveyard.ID = fields[0].Get<uint32>();
|
||||
Graveyard.Map = fields[1].Get<uint32>();
|
||||
Graveyard.x = fields[2].Get<float>();
|
||||
Graveyard.y = fields[3].Get<float>();
|
||||
Graveyard.z = fields[4].Get<float>();
|
||||
Graveyard.name = fields[5].Get<std::string>();
|
||||
|
||||
if (!Utf8toWStr(Graveyard.name, Graveyard.wnameLow))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Wrong UTF8 name for id {} in `game_graveyard` table, ignoring.", Graveyard.ID);
|
||||
continue;
|
||||
}
|
||||
|
||||
wstrToLower(Graveyard.wnameLow);
|
||||
|
||||
_graveyardStore[Graveyard.ID] = std::move(Graveyard);
|
||||
|
||||
++Count;
|
||||
} while (result->NextRow());
|
||||
|
||||
LOG_INFO("server.loading", ">> Loaded {} Graveyard in {} ms", Count, GetMSTimeDiffToNow(oldMSTime));
|
||||
LOG_INFO("server.loading", " ");
|
||||
}
|
||||
|
||||
GraveyardStruct const* Graveyard::GetGraveyard(uint32 ID) const
|
||||
{
|
||||
GraveyardContainer::const_iterator itr = _graveyardStore.find(ID);
|
||||
if (itr != _graveyardStore.end())
|
||||
return &itr->second;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GraveyardStruct const* Graveyard::GetDefaultGraveyard(TeamId teamId)
|
||||
{
|
||||
enum DefaultGraveyard
|
||||
{
|
||||
HORDE_GRAVEYARD = 10, // Crossroads
|
||||
ALLIANCE_GRAVEYARD = 4, // Westfall
|
||||
};
|
||||
|
||||
return GetGraveyard(teamId == TEAM_HORDE ? HORDE_GRAVEYARD : ALLIANCE_GRAVEYARD);
|
||||
}
|
||||
|
||||
GraveyardStruct const* Graveyard::GetClosestGraveyard(Player* player, TeamId teamId, bool nearCorpse)
|
||||
{
|
||||
uint32 graveyardOverride = 0;
|
||||
sScriptMgr->OnPlayerBeforeChooseGraveyard(player, teamId, nearCorpse, graveyardOverride);
|
||||
if (graveyardOverride)
|
||||
{
|
||||
return GetGraveyard(graveyardOverride);
|
||||
}
|
||||
|
||||
WorldLocation loc = player->GetWorldLocation();
|
||||
|
||||
if (nearCorpse)
|
||||
{
|
||||
loc = player->GetCorpseLocation();
|
||||
}
|
||||
|
||||
uint32 mapId = loc.GetMapId();
|
||||
float x = loc.GetPositionX();
|
||||
float y = loc.GetPositionY();
|
||||
float z = loc.GetPositionZ();
|
||||
|
||||
uint32 zoneId = 0;
|
||||
uint32 areaId = 0;
|
||||
player->GetZoneAndAreaId(zoneId, areaId);
|
||||
|
||||
if (!zoneId && !areaId)
|
||||
{
|
||||
if (z > -500)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "GetClosestGraveyard: unable to find zoneId and areaId for map {} coords ({}, {}, {})", mapId, x, y, z);
|
||||
return GetDefaultGraveyard(teamId);
|
||||
}
|
||||
}
|
||||
|
||||
// Simulate std. algorithm:
|
||||
// found some graveyard associated to (ghost_zone, ghost_map)
|
||||
//
|
||||
// if mapId == graveyard.mapId (ghost in plain zone or city or battleground) and search graveyard at same map
|
||||
// then check faction
|
||||
// if mapId != graveyard.mapId (ghost in instance) and search any graveyard associated
|
||||
// then check faction
|
||||
|
||||
// Fetch the graveyards linked to the areaId first, presumably the closer ones.
|
||||
GraveyardMapBounds range = GraveyardStore.equal_range(areaId);
|
||||
|
||||
// No graveyards linked to the area, search zone.
|
||||
if (range.first == range.second)
|
||||
{
|
||||
range = GraveyardStore.equal_range(zoneId);
|
||||
}
|
||||
else // Found a graveyard linked to the area, check if it's a valid one.
|
||||
{
|
||||
GraveyardData const& graveyardLink = range.first->second;
|
||||
|
||||
if (!graveyardLink.IsNeutralOrFriendlyToTeam(teamId))
|
||||
{
|
||||
// Not a friendly or neutral graveyard, search zone.
|
||||
range = GraveyardStore.equal_range(zoneId);
|
||||
}
|
||||
}
|
||||
|
||||
MapEntry const* map = sMapStore.LookupEntry(mapId);
|
||||
|
||||
// not need to check validity of map object; MapId _MUST_ be valid here
|
||||
if (range.first == range.second && !map->IsBattlegroundOrArena())
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `graveyard_zone` incomplete: Zone {} Team {} does not have a linked graveyard.", zoneId, teamId);
|
||||
return GetDefaultGraveyard(teamId);
|
||||
}
|
||||
|
||||
// at corpse map
|
||||
bool foundNear = false;
|
||||
float distNear = 10000;
|
||||
GraveyardStruct const* entryNear = nullptr;
|
||||
|
||||
// at entrance map for corpse map
|
||||
bool foundEntr = false;
|
||||
float distEntr = 10000;
|
||||
GraveyardStruct const* entryEntr = nullptr;
|
||||
|
||||
// some where other
|
||||
GraveyardStruct const* entryFar = nullptr;
|
||||
|
||||
MapEntry const* mapEntry = sMapStore.LookupEntry(mapId);
|
||||
|
||||
for (; range.first != range.second; ++range.first)
|
||||
{
|
||||
GraveyardData const& graveyardLink = range.first->second;
|
||||
GraveyardStruct const* entry = GetGraveyard(graveyardLink.safeLocId);
|
||||
if (!entry)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `graveyard_zone` has record for not existing `game_graveyard` table {}, skipped.", graveyardLink.safeLocId);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip enemy faction graveyard.
|
||||
if (!graveyardLink.IsNeutralOrFriendlyToTeam(teamId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip Archerus graveyards if the player isn't a Death Knight.
|
||||
enum DeathKnightGraveyards
|
||||
{
|
||||
GRAVEYARD_EBON_HOLD = 1369,
|
||||
GRAVEYARD_ARCHERUS = 1405
|
||||
};
|
||||
|
||||
if (!player->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_GRAVEYARD) && (graveyardLink.safeLocId == GRAVEYARD_EBON_HOLD || graveyardLink.safeLocId == GRAVEYARD_ARCHERUS))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// find now nearest graveyard at other map
|
||||
if (mapId != entry->Map)
|
||||
{
|
||||
// if find graveyard at different map from where entrance placed (or no entrance data), use any first
|
||||
if (!mapEntry
|
||||
|| mapEntry->entrance_map < 0
|
||||
|| uint32(mapEntry->entrance_map) != entry->Map
|
||||
|| (mapEntry->entrance_x == 0 && mapEntry->entrance_y == 0))
|
||||
{
|
||||
// not have any corrdinates for check distance anyway
|
||||
entryFar = entry;
|
||||
continue;
|
||||
}
|
||||
|
||||
// at entrance map calculate distance (2D);
|
||||
float dist2 = (entry->x - mapEntry->entrance_x) * (entry->x - mapEntry->entrance_x)
|
||||
+ (entry->y - mapEntry->entrance_y) * (entry->y - mapEntry->entrance_y);
|
||||
if (foundEntr)
|
||||
{
|
||||
if (dist2 < distEntr)
|
||||
{
|
||||
distEntr = dist2;
|
||||
entryEntr = entry;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foundEntr = true;
|
||||
distEntr = dist2;
|
||||
entryEntr = entry;
|
||||
}
|
||||
}
|
||||
// find now nearest graveyard at same map
|
||||
else
|
||||
{
|
||||
float dist2 = (entry->x - x) * (entry->x - x) + (entry->y - y) * (entry->y - y) + (entry->z - z) * (entry->z - z);
|
||||
if (foundNear)
|
||||
{
|
||||
if (dist2 < distNear)
|
||||
{
|
||||
distNear = dist2;
|
||||
entryNear = entry;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foundNear = true;
|
||||
distNear = dist2;
|
||||
entryNear = entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (entryNear)
|
||||
return entryNear;
|
||||
|
||||
if (entryEntr)
|
||||
return entryEntr;
|
||||
|
||||
return entryFar;
|
||||
}
|
||||
|
||||
GraveyardData const* Graveyard::FindGraveyardData(uint32 id, uint32 zoneId)
|
||||
{
|
||||
GraveyardMapBounds range = GraveyardStore.equal_range(zoneId);
|
||||
for (; range.first != range.second; ++range.first)
|
||||
{
|
||||
GraveyardData const& data = range.first->second;
|
||||
if (data.safeLocId == id)
|
||||
return &data;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Graveyard::AddGraveyardLink(uint32 id, uint32 zoneId, TeamId teamId, bool persist /*= true*/)
|
||||
{
|
||||
if (FindGraveyardData(id, zoneId))
|
||||
return false;
|
||||
|
||||
// add link to loaded data
|
||||
GraveyardData data;
|
||||
data.safeLocId = id;
|
||||
data.teamId = teamId;
|
||||
|
||||
GraveyardStore.insert(WGGraveyardContainer::value_type(zoneId, data));
|
||||
|
||||
// add link to DB
|
||||
if (persist)
|
||||
{
|
||||
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_GRAVEYARD_ZONE);
|
||||
|
||||
stmt->SetData(0, id);
|
||||
stmt->SetData(1, zoneId);
|
||||
// Xinef: DB Data compatibility...
|
||||
stmt->SetData(2, uint16(teamId == TEAM_NEUTRAL ? 0 : (teamId == TEAM_ALLIANCE ? ALLIANCE : HORDE)));
|
||||
|
||||
WorldDatabase.Execute(stmt);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Graveyard::RemoveGraveyardLink(uint32 id, uint32 zoneId, TeamId teamId, bool persist /*= false*/)
|
||||
{
|
||||
GraveyardMapBoundsNonConst range = GraveyardStore.equal_range(zoneId);
|
||||
if (range.first == range.second)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `graveyard_zone` incomplete: Zone {} Team {} does not have a linked graveyard.", zoneId, teamId);
|
||||
return;
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
|
||||
for (; range.first != range.second; ++range.first)
|
||||
{
|
||||
GraveyardData& data = range.first->second;
|
||||
|
||||
// skip not matching safezone id
|
||||
if (data.safeLocId != id)
|
||||
continue;
|
||||
|
||||
// skip enemy faction graveyard at same map (normal area, city, or battleground)
|
||||
// team == 0 case can be at call from .neargrave
|
||||
if (data.teamId != TEAM_NEUTRAL && teamId != TEAM_NEUTRAL && data.teamId != teamId)
|
||||
continue;
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// no match, return
|
||||
if (!found)
|
||||
return;
|
||||
|
||||
// remove from links
|
||||
GraveyardStore.erase(range.first);
|
||||
|
||||
// remove link from DB
|
||||
if (persist)
|
||||
{
|
||||
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GRAVEYARD_ZONE);
|
||||
|
||||
stmt->SetData(0, id);
|
||||
stmt->SetData(1, zoneId);
|
||||
// Xinef: DB Data compatibility...
|
||||
stmt->SetData(2, uint16(teamId == TEAM_NEUTRAL ? 0 : (teamId == TEAM_ALLIANCE ? ALLIANCE : HORDE)));
|
||||
|
||||
WorldDatabase.Execute(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
void Graveyard::LoadGraveyardZones()
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
|
||||
GraveyardStore.clear(); // need for reload case
|
||||
|
||||
// 0 1 2
|
||||
QueryResult result = WorldDatabase.Query("SELECT ID, GhostZone, Faction FROM graveyard_zone");
|
||||
|
||||
if (!result)
|
||||
{
|
||||
LOG_WARN("server.loading", ">> Loaded 0 Graveyard-Zone Links. DB Table `graveyard_zone` Is Empty.");
|
||||
LOG_INFO("server.loading", " ");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 count = 0;
|
||||
|
||||
do
|
||||
{
|
||||
++count;
|
||||
|
||||
Field* fields = result->Fetch();
|
||||
|
||||
uint32 safeLocId = fields[0].Get<uint32>();
|
||||
uint32 zoneId = fields[1].Get<uint32>();
|
||||
uint32 team = fields[2].Get<uint16>();
|
||||
TeamId teamId = team == 0 ? TEAM_NEUTRAL : (team == ALLIANCE ? TEAM_ALLIANCE : TEAM_HORDE);
|
||||
|
||||
GraveyardStruct const* entry = GetGraveyard(safeLocId);
|
||||
if (!entry)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for not existing `game_graveyard` table {}, skipped.", safeLocId);
|
||||
continue;
|
||||
}
|
||||
|
||||
AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(zoneId);
|
||||
if (!areaEntry)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for not existing zone id ({}), skipped.", zoneId);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (team != 0 && team != HORDE && team != ALLIANCE)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for non player faction ({}), skipped.", team);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!AddGraveyardLink(safeLocId, zoneId, teamId, false))
|
||||
LOG_ERROR("sql.sql", "Table `graveyard_zone` has a duplicate record for Graveyard (ID: {}) and Zone (ID: {}), skipped.", safeLocId, zoneId);
|
||||
} while (result->NextRow());
|
||||
|
||||
LOG_INFO("server.loading", ">> Loaded {} Graveyard-Zone Links in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
|
||||
LOG_INFO("server.loading", " ");
|
||||
}
|
||||
|
||||
GraveyardStruct const* Graveyard::GetGraveyard(const std::string& name) const
|
||||
{
|
||||
// explicit name case
|
||||
std::wstring wname;
|
||||
if (!Utf8toWStr(name, wname))
|
||||
return nullptr;
|
||||
|
||||
// converting string that we try to find to lower case
|
||||
wstrToLower(wname);
|
||||
|
||||
// Alternative first GameTele what contains wnameLow as substring in case no GameTele location found
|
||||
const GraveyardStruct* alt = nullptr;
|
||||
for (GraveyardContainer::const_iterator itr = _graveyardStore.begin(); itr != _graveyardStore.end(); ++itr)
|
||||
{
|
||||
if (itr->second.wnameLow == wname)
|
||||
return &itr->second;
|
||||
else if (!alt && itr->second.wnameLow.find(wname) != std::wstring::npos)
|
||||
alt = &itr->second;
|
||||
}
|
||||
|
||||
return alt;
|
||||
}
|
||||
76
src/server/game/Misc/GameGraveyard.h
Normal file
76
src/server/game/Misc/GameGraveyard.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _GAMEGRAVEYARD_H_
|
||||
#define _GAMEGRAVEYARD_H_
|
||||
|
||||
#include "Player.h"
|
||||
#include "SharedDefines.h"
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
|
||||
struct GraveyardStruct
|
||||
{
|
||||
uint32 ID;
|
||||
uint32 Map;
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
std::string name;
|
||||
std::wstring wnameLow;
|
||||
};
|
||||
|
||||
struct GraveyardData
|
||||
{
|
||||
uint32 safeLocId;
|
||||
TeamId teamId;
|
||||
|
||||
[[nodiscard]] bool IsNeutralOrFriendlyToTeam(TeamId playerTeamId) const { return teamId == TEAM_NEUTRAL || playerTeamId == TEAM_NEUTRAL || teamId == playerTeamId; }
|
||||
};
|
||||
|
||||
typedef std::multimap<uint32, GraveyardData> WGGraveyardContainer;
|
||||
typedef std::pair<WGGraveyardContainer::const_iterator, WGGraveyardContainer::const_iterator> GraveyardMapBounds;
|
||||
typedef std::pair<WGGraveyardContainer::iterator, WGGraveyardContainer::iterator> GraveyardMapBoundsNonConst;
|
||||
|
||||
class Graveyard
|
||||
{
|
||||
public:
|
||||
static Graveyard* instance();
|
||||
|
||||
typedef std::unordered_map<uint32, GraveyardStruct> GraveyardContainer;
|
||||
|
||||
GraveyardStruct const* GetGraveyard(uint32 ID) const;
|
||||
GraveyardStruct const* GetGraveyard(const std::string& name) const;
|
||||
GraveyardStruct const* GetDefaultGraveyard(TeamId teamId);
|
||||
GraveyardStruct const* GetClosestGraveyard(Player* player, TeamId teamId, bool nearCorpse = false);
|
||||
GraveyardData const* FindGraveyardData(uint32 id, uint32 zone);
|
||||
GraveyardContainer const& GetGraveyardData() const { return _graveyardStore; }
|
||||
bool AddGraveyardLink(uint32 id, uint32 zoneId, TeamId teamId, bool persist = true);
|
||||
void RemoveGraveyardLink(uint32 id, uint32 zoneId, TeamId teamId, bool persist = false);
|
||||
void LoadGraveyardZones();
|
||||
void LoadGraveyardFromDB();
|
||||
|
||||
private:
|
||||
GraveyardContainer _graveyardStore;
|
||||
|
||||
// for wintergrasp only
|
||||
WGGraveyardContainer GraveyardStore;
|
||||
};
|
||||
|
||||
#define sGraveyard Graveyard::instance()
|
||||
|
||||
#endif // _GAMEGRAVEYARD_H_
|
||||
Reference in New Issue
Block a user