Scripts/Commands: .character command conversion

(cherry picked from commit 00dd337b5d)
This commit is contained in:
Treeston
2020-09-07 15:38:23 +02:00
committed by Shauren
parent cb2a284359
commit 1e9d60b4ac
7 changed files with 263 additions and 426 deletions

View File

@@ -101,6 +101,12 @@ void PreparedStatementBase::setString(const uint8 index, const std::string& valu
statement_data[index].data = value;
}
void PreparedStatementBase::setStringView(const uint8 index, const std::string_view value)
{
ASSERT(index < statement_data.size());
statement_data[index].data.emplace<std::string>(value);
}
void PreparedStatementBase::setBinary(const uint8 index, const std::vector<uint8>& value)
{
ASSERT(index < statement_data.size());

View File

@@ -76,6 +76,7 @@ class TC_DATABASE_API PreparedStatementBase
void setFloat(const uint8 index, const float value);
void setDouble(const uint8 index, const double value);
void setString(const uint8 index, const std::string& value);
void setStringView(const uint8 index, const std::string_view value);
void setBinary(const uint8 index, const std::vector<uint8>& value);
template <size_t Size>
void setBinary(const uint8 index, std::array<uint8, Size> const& value)

View File

@@ -284,10 +284,10 @@ AccountOpResult AccountMgr::ChangeRegEmail(uint32 accountId, std::string newEmai
return AccountOpResult::AOR_OK;
}
uint32 AccountMgr::GetId(std::string const& username)
uint32 AccountMgr::GetId(std::string_view username)
{
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_ACCOUNT_ID_BY_USERNAME);
stmt->setString(0, username);
stmt->setStringView(0, username);
PreparedQueryResult result = LoginDatabase.Query(stmt);
return (result) ? (*result)[0].GetUInt32() : 0;

View File

@@ -67,7 +67,7 @@ class TC_GAME_API AccountMgr
static bool CheckPassword(uint32 accountId, std::string password);
static bool CheckEmail(uint32 accountId, std::string newEmail);
static uint32 GetId(std::string const& username);
static uint32 GetId(std::string_view username);
static uint32 GetSecurity(uint32 accountId, int32 realmId);
[[nodiscard]] static QueryCallback GetSecurityAsync(uint32 accountId, int32 realmId, std::function<void(uint32)> callback);
static bool GetName(uint32 accountId, std::string& name);

View File

@@ -17,6 +17,7 @@
#include "ChatCommandTags.h"
#include "AccountMgr.h"
#include "CharacterCache.h"
#include "Chat.h"
#include "ChatCommandArgs.h"
@@ -55,22 +56,65 @@ Optional<std::string_view> Trinity::ChatCommands::QuotedString::TryConsume(std::
return std::nullopt;
}
Optional<std::string_view> Trinity::ChatCommands::PlayerIdentifier::TryConsume(std::string_view args)
Optional<std::string_view> Trinity::ChatCommands::AccountIdentifier::TryConsume(std::string_view args)
{
Variant<Hyperlink<player>, std::string_view> name;
Optional<std::string_view> next = Trinity::Impl::ChatCommands::ArgInfo<decltype(name)>::TryConsume(name, args);
std::string_view text;
Optional<std::string_view> next = Trinity::Impl::ChatCommands::ArgInfo<std::string_view>::TryConsume(text, args);
if (!next)
return std::nullopt;
_name = static_cast<std::string_view>(name);
normalizePlayerName(_name);
// first try parsing as account name
_name.assign(text);
if (!Utf8ToUpperOnlyLatin(_name))
return std::nullopt;
_id = AccountMgr::GetId(_name);
if (_id) // account with name exists, we are done
return next;
if ((_player = ObjectAccessor::FindPlayerByName(_name)))
_guid = _player->GetGUID();
else if (!(_guid = sCharacterCache->GetCharacterGuidByName(_name)))
// try parsing as account id instead
Optional<uint32> id = Trinity::StringTo<uint32>(text, 10);
if (!id)
return std::nullopt;
_id = *id;
if (AccountMgr::GetName(_id, _name))
return next;
else
return std::nullopt;
}
Optional<std::string_view> Trinity::ChatCommands::PlayerIdentifier::TryConsume(std::string_view args)
{
Variant<Hyperlink<player>, ObjectGuid::LowType, std::string_view> val;
Optional<std::string_view> next = Trinity::Impl::ChatCommands::ArgInfo<decltype(val)>::TryConsume(val, args);
if (!next)
return std::nullopt;
return next;
if (val.holds_alternative<ObjectGuid::LowType>())
{
_guid = ObjectGuid::Create<HighGuid::Player>(val.get<ObjectGuid::LowType>());
if ((_player = ObjectAccessor::FindPlayerByLowGUID(_guid.GetCounter())))
_name = _player->GetName();
else if (!sCharacterCache->GetCharacterNameByGuid(_guid, _name))
return std::nullopt;
return next;
}
else
{
if (val.holds_alternative<Hyperlink<player>>())
_name.assign(static_cast<std::string_view>(val.get<Hyperlink<player>>()));
else
_name.assign(val.get<std::string_view>());
if (!normalizePlayerName(_name))
return std::nullopt;
if ((_player = ObjectAccessor::FindPlayerByName(_name)))
_guid = _player->GetGUID();
else if (!(_guid = sCharacterCache->GetCharacterGuidByName(_name)))
return std::nullopt;
return next;
}
}
Trinity::ChatCommands::PlayerIdentifier::PlayerIdentifier(Player& player)

View File

@@ -120,6 +120,24 @@ namespace Trinity::ChatCommands
TC_GAME_API Optional<std::string_view> TryConsume(std::string_view args);
};
struct TC_GAME_API AccountIdentifier : Trinity::Impl::ChatCommands::ContainerTag
{
using value_type = uint32;
operator uint32() const { return _id; }
operator std::string const& () const { return _name; }
operator std::string_view() const { return { _name }; }
uint32 GetID() const { return _id; }
std::string const& GetName() const { return _name; }
Optional<std::string_view> TryConsume(std::string_view args);
private:
uint32 _id;
std::string _name;
};
struct TC_GAME_API PlayerIdentifier : Trinity::Impl::ChatCommands::ContainerTag
{
using value_type = Player*;
@@ -137,7 +155,8 @@ namespace Trinity::ChatCommands
std::string const& GetName() { return _name; }
ObjectGuid GetGUID() const { return _guid; }
Player* GetPlayer() const { return _player; }
bool IsConnected() const { return (_player != nullptr); }
Player* GetConnectedPlayer() const { return _player; }
Optional<std::string_view> TryConsume(std::string_view args);

View File

@@ -38,6 +38,7 @@ EndScriptData */
#include "WorldSession.h"
#include <sstream>
using namespace Trinity::ChatCommands;
class character_commandscript : public CommandScript
{
public:
@@ -66,7 +67,7 @@ public:
{ "changeaccount", rbac::RBAC_PERM_COMMAND_CHARACTER_CHANGEACCOUNT, true, &HandleCharacterChangeAccountCommand, "", },
{ "deleted", rbac::RBAC_PERM_COMMAND_CHARACTER_DELETED, true, nullptr, "", characterDeletedCommandTable },
{ "erase", rbac::RBAC_PERM_COMMAND_CHARACTER_ERASE, true, &HandleCharacterEraseCommand, "", },
{ "level", rbac::RBAC_PERM_COMMAND_CHARACTER_LEVEL, true, &HandleCharacterLevelCommand, "", },
{ "level", rbac::RBAC_PERM_COMMAND_CHARACTER_LEVEL, true, &HandleLevelUpCommand, "", },
{ "rename", rbac::RBAC_PERM_COMMAND_CHARACTER_RENAME, true, &HandleCharacterRenameCommand, "", },
{ "reputation", rbac::RBAC_PERM_COMMAND_CHARACTER_REPUTATION, true, &HandleCharacterReputationCommand, "", },
{ "titles", rbac::RBAC_PERM_COMMAND_CHARACTER_TITLES, true, &HandleCharacterTitlesCommand, "", },
@@ -100,7 +101,7 @@ public:
* @param searchString the search string which either contains a player GUID or a part fo the character-name
* @return returns false if there was a problem while selecting the characters (e.g. player name not normalizeable)
*/
static bool GetDeletedCharacterInfoList(DeletedInfoList& foundList, std::string searchString)
static bool GetDeletedCharacterInfoList(DeletedInfoList& foundList, std::string& searchString)
{
PreparedQueryResult result;
CharacterDatabasePreparedStatement* stmt;
@@ -231,45 +232,21 @@ public:
sCharacterCache->UpdateCharacterInfoDeleted(delInfo.guid, false, &delInfo.name);
}
static void HandleCharacterLevel(Player* player, ObjectGuid playerGuid, uint32 oldLevel, uint32 newLevel, ChatHandler* handler)
static bool HandleCharacterTitlesCommand(ChatHandler* handler, Optional<PlayerIdentifier> player)
{
if (player)
if (!player)
player = PlayerIdentifier::FromTargetOrSelf(handler);
if (!player || !player->IsConnected())
{
player->GiveLevel(newLevel);
player->InitTalentForLevel();
player->SetXP(0);
if (handler->needReportToTarget(player))
{
if (oldLevel == newLevel)
ChatHandler(player->GetSession()).PSendSysMessage(LANG_YOURS_LEVEL_PROGRESS_RESET, handler->GetNameLink().c_str());
else if (oldLevel < newLevel)
ChatHandler(player->GetSession()).PSendSysMessage(LANG_YOURS_LEVEL_UP, handler->GetNameLink().c_str(), newLevel);
else // if (oldlevel > newlevel)
ChatHandler(player->GetSession()).PSendSysMessage(LANG_YOURS_LEVEL_DOWN, handler->GetNameLink().c_str(), newLevel);
}
}
else
{
// Update level and reset XP, everything else will be updated at login
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_LEVEL);
stmt->setUInt8(0, uint8(newLevel));
stmt->setUInt64(1, playerGuid.GetCounter());
CharacterDatabase.Execute(stmt);
}
}
static bool HandleCharacterTitlesCommand(ChatHandler* handler, char const* args)
{
if (!*args)
handler->SendSysMessage(LANG_PLAYER_NOT_FOUND);
handler->SetSentErrorMessage(true);
return false;
}
Player* target;
if (!handler->extractPlayerTarget((char*)args, &target))
return false;
Player const* target = player->GetConnectedPlayer();
LocaleConstant loc = handler->GetSessionDbcLocale();
char const* targetName = target->GetName().c_str();
std::string const& targetName = player->GetName();
char const* knownStr = handler->GetTrinityString(LANG_KNOWN);
// Search in CharTitles.dbc
@@ -279,21 +256,23 @@ public:
if (titleInfo && target->HasTitle(titleInfo))
{
std::string name = target->GetNativeGender() == GENDER_MALE ? titleInfo->Name[loc] : titleInfo->Name1[loc];
if (name.empty())
char const* name = target->GetNativeGender() == GENDER_MALE ? titleInfo->Name[loc] : titleInfo->Name1[loc];
if (!*name)
name = (target->GetNativeGender() == GENDER_MALE ? titleInfo->Name[sWorld->GetDefaultDbcLocale()] : titleInfo->Name1[sWorld->GetDefaultDbcLocale()]);
if (!*name)
continue;
char const* activeStr = *target->m_playerData->PlayerTitle == titleInfo->MaskID
? handler->GetTrinityString(LANG_ACTIVE)
: "";
char const* activeStr = "";
if (*target->m_playerData->PlayerTitle == titleInfo->MaskID)
activeStr = handler->GetTrinityString(LANG_ACTIVE);
std::string titleNameStr = Trinity::StringFormat(name.c_str(), targetName);
std::string titleName = Trinity::StringFormat(name, targetName.c_str());
// send title in "id (idx:idx) - [namedlink locale]" format
if (handler->GetSession())
handler->PSendSysMessage(LANG_TITLE_LIST_CHAT, id, titleInfo->MaskID, id, titleNameStr.c_str(), localeNames[loc], knownStr, activeStr);
handler->PSendSysMessage(LANG_TITLE_LIST_CHAT, id, titleInfo->MaskID, id, titleName.c_str(), localeNames[loc], knownStr, activeStr);
else
handler->PSendSysMessage(LANG_TITLE_LIST_CONSOLE, id, titleInfo->MaskID, name.c_str(), localeNames[loc], knownStr, activeStr);
handler->PSendSysMessage(LANG_TITLE_LIST_CONSOLE, id, titleInfo->MaskID, name, localeNames[loc], knownStr, activeStr);
}
}
@@ -301,38 +280,22 @@ public:
}
//rename characters
static bool HandleCharacterRenameCommand(ChatHandler* handler, char const* args)
static bool HandleCharacterRenameCommand(ChatHandler* handler, Optional<PlayerIdentifier> player, Optional<std::string_view> newNameV)
{
Player* target;
ObjectGuid targetGuid;
std::string targetName;
if (!handler->extractPlayerTarget((char*)args, &target, &targetGuid, &targetName))
if (!player && newNameV)
return false;
char const* newNameStr = strtok(nullptr, " ");
if (!player)
player = PlayerIdentifier::FromTarget(handler);
if (!player)
return false;
if (newNameStr)
if (handler->HasLowerSecurity(nullptr, player->GetGUID()))
return false;
if (newNameV)
{
std::string playerOldName;
std::string newName = newNameStr;
if (target)
{
// check online security
if (handler->HasLowerSecurity(target, ObjectGuid::Empty))
return false;
playerOldName = target->GetName();
}
else
{
// check offline security
if (handler->HasLowerSecurity(nullptr, targetGuid))
return false;
sCharacterCache->GetCharacterNameByGuid(targetGuid, playerOldName);
}
std::string newName{ *newNameV };
if (!normalizePlayerName(newName))
{
handler->SendSysMessage(LANG_BAD_VALUE);
@@ -340,7 +303,7 @@ public:
return false;
}
if (ObjectMgr::CheckPlayerName(newName, target ? target->GetSession()->GetSessionDbcLocale() : sWorld->GetDefaultDbcLocale(), true) != CHAR_NAME_SUCCESS)
if (ObjectMgr::CheckPlayerName(newName, player->IsConnected() ? player->GetConnectedPlayer()->GetSession()->GetSessionDbcLocale() : sWorld->GetDefaultDbcLocale(), true) != CHAR_NAME_SUCCESS)
{
handler->SendSysMessage(LANG_BAD_VALUE);
handler->SetSentErrorMessage(true);
@@ -369,10 +332,10 @@ public:
// Remove declined name from db
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_DECLINED_NAME);
stmt->setUInt64(0, targetGuid.GetCounter());
stmt->setUInt64(0, player->GetGUID().GetCounter());
CharacterDatabase.Execute(stmt);
if (target)
if (Player* target = player->GetConnectedPlayer())
{
target->SetName(newName);
@@ -383,45 +346,40 @@ public:
{
stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_NAME_BY_GUID);
stmt->setString(0, newName);
stmt->setUInt64(1, targetGuid.GetCounter());
stmt->setUInt64(1, player->GetGUID().GetCounter());
CharacterDatabase.Execute(stmt);
}
sCharacterCache->UpdateCharacterData(targetGuid, newName);
sCharacterCache->UpdateCharacterData(*player, newName);
handler->PSendSysMessage(LANG_RENAME_PLAYER_WITH_NEW_NAME, playerOldName.c_str(), newName.c_str());
handler->PSendSysMessage(LANG_RENAME_PLAYER_WITH_NEW_NAME, player->GetName().c_str(), newName.c_str());
if (WorldSession* session = handler->GetSession())
{
if (Player* player = session->GetPlayer())
sLog->outCommand(session->GetAccountId(), "GM %s (Account: %u) forced rename %s to player %s (Account: %u)", player->GetName().c_str(), session->GetAccountId(), newName.c_str(), playerOldName.c_str(), sCharacterCache->GetCharacterAccountIdByGuid(targetGuid));
sLog->outCommand(session->GetAccountId(), "GM %s (Account: %u) forced rename %s to player %s (Account: %u)", player->GetName().c_str(), session->GetAccountId(), newName.c_str(), player->GetName().c_str(), sCharacterCache->GetCharacterAccountIdByGuid(player->GetGUID()));
}
else
sLog->outCommand(0, "CONSOLE forced rename '%s' to '%s' (%s)", playerOldName.c_str(), newName.c_str(), targetGuid.ToString().c_str());
sLog->outCommand(0, "CONSOLE forced rename '%s' to '%s' (%s)", player->GetName().c_str(), newName.c_str(), player->GetGUID().ToString().c_str());
}
else
{
if (target)
if (Player* target = player->GetConnectedPlayer())
{
// check online security
if (handler->HasLowerSecurity(target, ObjectGuid::Empty))
return false;
handler->PSendSysMessage(LANG_RENAME_PLAYER, handler->GetNameLink(target).c_str());
target->SetAtLoginFlag(AT_LOGIN_RENAME);
}
else
{
// check offline security
if (handler->HasLowerSecurity(nullptr, targetGuid))
if (handler->HasLowerSecurity(nullptr, player->GetGUID()))
return false;
std::string oldNameLink = handler->playerLink(targetName);
handler->PSendSysMessage(LANG_RENAME_PLAYER_GUID, oldNameLink.c_str(), targetGuid.ToString().c_str());
handler->PSendSysMessage(LANG_RENAME_PLAYER_GUID, handler->playerLink(player->GetName()).c_str(), player->GetGUID().ToString().c_str());
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG);
stmt->setUInt16(0, uint16(AT_LOGIN_RENAME));
stmt->setUInt64(1, targetGuid.GetCounter());
stmt->setUInt64(1, player->GetGUID().GetCounter());
CharacterDatabase.Execute(stmt);
}
}
@@ -429,145 +387,87 @@ public:
return true;
}
static bool HandleCharacterLevelCommand(ChatHandler* handler, char const* args)
{
char* nameStr;
char* levelStr;
handler->extractOptFirstArg((char*)args, &nameStr, &levelStr);
if (!levelStr)
return false;
// exception opt second arg: .character level $name
if (isalpha(levelStr[0]))
{
nameStr = levelStr;
levelStr = nullptr; // current level will used
}
Player* target;
ObjectGuid targetGuid;
std::string targetName;
if (!handler->extractPlayerTarget(nameStr, &target, &targetGuid, &targetName))
return false;
int32 oldlevel = target ? target->GetLevel() : sCharacterCache->GetCharacterLevelByGuid(targetGuid);
int32 newlevel = levelStr ? atoi(levelStr) : oldlevel;
if (newlevel < 1)
return false; // invalid level
if (newlevel > STRONG_MAX_LEVEL) // hardcoded maximum level
newlevel = STRONG_MAX_LEVEL;
HandleCharacterLevel(target, targetGuid, oldlevel, newlevel, handler);
if (!handler->GetSession() || handler->GetSession()->GetPlayer() != target) // including player == NULL
{
std::string nameLink = handler->playerLink(targetName);
handler->PSendSysMessage(LANG_YOU_CHANGE_LVL, nameLink.c_str(), newlevel);
}
return true;
}
// customize characters
static bool HandleCharacterCustomizeCommand(ChatHandler* handler, char const* args)
static bool HandleCharacterCustomizeCommand(ChatHandler* handler, Optional<PlayerIdentifier> player)
{
Player* target;
ObjectGuid targetGuid;
std::string targetName;
if (!handler->extractPlayerTarget((char*)args, &target, &targetGuid, &targetName))
if (!player)
player = PlayerIdentifier::FromTarget(handler);
if (!player)
return false;
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG);
stmt->setUInt16(0, uint16(AT_LOGIN_CUSTOMIZE));
if (target)
if (Player* target = player->GetConnectedPlayer())
{
handler->PSendSysMessage(LANG_CUSTOMIZE_PLAYER, handler->GetNameLink(target).c_str());
target->SetAtLoginFlag(AT_LOGIN_CUSTOMIZE);
stmt->setUInt64(1, target->GetGUID().GetCounter());
}
else
{
std::string oldNameLink = handler->playerLink(targetName);
stmt->setUInt64(1, targetGuid.GetCounter());
handler->PSendSysMessage(LANG_CUSTOMIZE_PLAYER_GUID, oldNameLink.c_str(), targetGuid.ToString().c_str());
handler->PSendSysMessage(LANG_CUSTOMIZE_PLAYER_GUID, handler->playerLink(player->GetName()).c_str(), player->GetGUID().GetCounter());
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG);
stmt->setUInt16(0, static_cast<uint16>(AT_LOGIN_CUSTOMIZE));
stmt->setUInt64(1, player->GetGUID().GetCounter());
CharacterDatabase.Execute(stmt);
}
CharacterDatabase.Execute(stmt);
return true;
}
static bool HandleCharacterChangeFactionCommand(ChatHandler* handler, char const* args)
static bool HandleCharacterChangeFactionCommand(ChatHandler* handler, Optional<PlayerIdentifier> player)
{
Player* target;
ObjectGuid targetGuid;
std::string targetName;
if (!handler->extractPlayerTarget((char*)args, &target, &targetGuid, &targetName))
if (!player)
player = PlayerIdentifier::FromTarget(handler);
if (!player)
return false;
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG);
stmt->setUInt16(0, uint16(AT_LOGIN_CHANGE_FACTION));
if (target)
if (Player* target = player->GetConnectedPlayer())
{
handler->PSendSysMessage(LANG_CUSTOMIZE_PLAYER, handler->GetNameLink(target).c_str());
target->SetAtLoginFlag(AT_LOGIN_CHANGE_FACTION);
stmt->setUInt64(1, target->GetGUID().GetCounter());
}
else
{
std::string oldNameLink = handler->playerLink(targetName);
handler->PSendSysMessage(LANG_CUSTOMIZE_PLAYER_GUID, oldNameLink.c_str(), targetGuid.ToString().c_str());
stmt->setUInt64(1, targetGuid.GetCounter());
handler->PSendSysMessage(LANG_CUSTOMIZE_PLAYER_GUID, handler->playerLink(player->GetName()).c_str(), player->GetGUID().GetCounter());
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG);
stmt->setUInt16(0, uint16(AT_LOGIN_CHANGE_FACTION));
stmt->setUInt64(1, player->GetGUID().GetCounter());
CharacterDatabase.Execute(stmt);
}
CharacterDatabase.Execute(stmt);
return true;
}
static bool HandleCharacterChangeRaceCommand(ChatHandler* handler, char const* args)
static bool HandleCharacterChangeRaceCommand(ChatHandler* handler, Optional<PlayerIdentifier> player)
{
Player* target;
ObjectGuid targetGuid;
std::string targetName;
if (!handler->extractPlayerTarget((char*)args, &target, &targetGuid, &targetName))
if (!player)
player = PlayerIdentifier::FromTarget(handler);
if (!player)
return false;
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG);
stmt->setUInt16(0, uint16(AT_LOGIN_CHANGE_RACE));
if (target)
if (Player* target = player->GetConnectedPlayer())
{
/// @todo add text into database
handler->PSendSysMessage(LANG_CUSTOMIZE_PLAYER, handler->GetNameLink(target).c_str());
target->SetAtLoginFlag(AT_LOGIN_CHANGE_RACE);
stmt->setUInt64(1, target->GetGUID().GetCounter());
}
else
{
std::string oldNameLink = handler->playerLink(targetName);
/// @todo add text into database
handler->PSendSysMessage(LANG_CUSTOMIZE_PLAYER_GUID, oldNameLink.c_str(), targetGuid.ToString().c_str());
stmt->setUInt64(1, targetGuid.GetCounter());
handler->PSendSysMessage(LANG_CUSTOMIZE_PLAYER_GUID, handler->playerLink(player->GetName()).c_str(), player->GetGUID().GetCounter());
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG);
stmt->setUInt16(0, uint16(AT_LOGIN_CHANGE_RACE));
stmt->setUInt64(1, player->GetGUID().GetCounter());
CharacterDatabase.Execute(stmt);
}
CharacterDatabase.Execute(stmt);
return true;
}
static bool HandleCharacterChangeAccountCommand(ChatHandler* handler, char const* args)
static bool HandleCharacterChangeAccountCommand(ChatHandler* handler, Optional<PlayerIdentifier> player, AccountIdentifier newAccount)
{
char* playerNameStr;
char* accountNameStr;
handler->extractOptFirstArg(const_cast<char*>(args), &playerNameStr, &accountNameStr);
if (!accountNameStr)
if (!player)
player = PlayerIdentifier::FromTarget(handler);
if (!player)
return false;
ObjectGuid targetGuid;
std::string targetName;
if (!handler->extractPlayerTarget(playerNameStr, nullptr, &targetGuid, &targetName))
return false;
CharacterCacheEntry const* characterInfo = sCharacterCache->GetCharacterCacheByGuid(targetGuid);
CharacterCacheEntry const* characterInfo = sCharacterCache->GetCharacterCacheByGuid(player->GetGUID());
if (!characterInfo)
{
handler->SendSysMessage(LANG_PLAYER_NOT_FOUND);
@@ -576,54 +476,33 @@ public:
}
uint32 oldAccountId = characterInfo->AccountId;
uint32 newAccountId = oldAccountId;
std::string accountName(accountNameStr);
if (!Utf8ToUpperOnlyLatin(accountName))
{
handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
handler->SetSentErrorMessage(true);
return false;
}
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_ID_BY_NAME);
stmt->setString(0, accountName);
if (PreparedQueryResult result = LoginDatabase.Query(stmt))
newAccountId = (*result)[0].GetUInt32();
else
{
handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
handler->SetSentErrorMessage(true);
return false;
}
// nothing to do :)
if (newAccountId == oldAccountId)
if (newAccount.GetID() == oldAccountId)
return true;
if (uint32 charCount = AccountMgr::GetCharactersCount(newAccountId))
if (uint32 charCount = AccountMgr::GetCharactersCount(newAccount.GetID()))
{
if (charCount >= sWorld->getIntConfig(CONFIG_CHARACTERS_PER_REALM))
{
handler->PSendSysMessage(LANG_ACCOUNT_CHARACTER_LIST_FULL, accountName.c_str(), newAccountId);
handler->PSendSysMessage(LANG_ACCOUNT_CHARACTER_LIST_FULL, newAccount.GetName().c_str(), newAccount.GetID());
handler->SetSentErrorMessage(true);
return false;
}
}
CharacterDatabasePreparedStatement* charStmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ACCOUNT_BY_GUID);
charStmt->setUInt32(0, newAccountId);
charStmt->setUInt32(1, targetGuid.GetCounter());
charStmt->setUInt32(0, newAccount.GetID());
charStmt->setUInt64(1, player->GetGUID().GetCounter());
CharacterDatabase.DirectExecute(charStmt);
sWorld->UpdateRealmCharCount(oldAccountId);
sWorld->UpdateRealmCharCount(newAccountId);
sWorld->UpdateRealmCharCount(newAccount.GetID());
sCharacterCache->UpdateCharacterAccountId(targetGuid, newAccountId);
sCharacterCache->UpdateCharacterAccountId(*player, newAccount.GetID());
handler->PSendSysMessage(LANG_CHANGEACCOUNT_SUCCESS, targetName.c_str(), accountName.c_str());
handler->PSendSysMessage(LANG_CHANGEACCOUNT_SUCCESS, player->GetName().c_str(), newAccount.GetName().c_str());
std::string logString = Trinity::StringFormat("changed ownership of player %s (%s) from account %u to account %u", targetName.c_str(), targetGuid.ToString().c_str(), oldAccountId, newAccountId);
std::string logString = Trinity::StringFormat("changed ownership of player %s (%s) from account %u to account %u", player->GetName().c_str(), player->GetGUID().ToString().c_str(), oldAccountId, newAccount.GetID());
if (WorldSession* session = handler->GetSession())
{
if (Player* player = session->GetPlayer())
@@ -634,12 +513,18 @@ public:
return true;
}
static bool HandleCharacterReputationCommand(ChatHandler* handler, char const* args)
static bool HandleCharacterReputationCommand(ChatHandler* handler, Optional<PlayerIdentifier> player)
{
Player* target;
if (!handler->extractPlayerTarget((char*)args, &target))
if (!player)
player = PlayerIdentifier::FromTargetOrSelf(handler);
if (!player || !player->IsConnected())
{
handler->SendSysMessage(LANG_PLAYER_NOT_FOUND);
handler->SetSentErrorMessage(true);
return false;
}
Player const* target = player->GetConnectedPlayer();
LocaleConstant loc = handler->GetSessionDbcLocale();
FactionStateList const& targetFSL = target->GetReputationMgr().GetStateList();
@@ -686,16 +571,20 @@ public:
*
* @param args the search string which either contains a player GUID or a part fo the character-name
*/
static bool HandleCharacterDeletedListCommand(ChatHandler* handler, char const* args)
static bool HandleCharacterDeletedListCommand(ChatHandler* handler, Optional<std::string_view> needleStr)
{
std::string needle;
if (needleStr)
needle.assign(*needleStr);
DeletedInfoList foundList;
if (!GetDeletedCharacterInfoList(foundList, args))
if (!GetDeletedCharacterInfoList(foundList, needle))
return false;
// if no characters have been found, output a warning
if (foundList.empty())
{
handler->SendSysMessage(LANG_CHARACTER_DELETED_LIST_EMPTY);
handler->SetSentErrorMessage(true);
return false;
}
@@ -715,59 +604,52 @@ public:
*
* @param args the search string which either contains a player GUID or a part of the character-name
*/
static bool HandleCharacterDeletedRestoreCommand(ChatHandler* handler, char const* args)
static bool HandleCharacterDeletedRestoreCommand(ChatHandler* handler, std::string needle, Optional<std::string_view> newCharName, Optional<AccountIdentifier> newAccount)
{
// It is required to submit at least one argument
if (!*args)
return false;
std::string searchString;
std::string newCharName;
uint32 newAccount = 0;
// GCC by some strange reason fail build code without temporary variable
std::istringstream params(args);
params >> searchString >> newCharName >> newAccount;
DeletedInfoList foundList;
if (!GetDeletedCharacterInfoList(foundList, searchString))
if (!GetDeletedCharacterInfoList(foundList, needle))
return false;
if (foundList.empty())
{
handler->SendSysMessage(LANG_CHARACTER_DELETED_LIST_EMPTY);
handler->SetSentErrorMessage(true);
return false;
}
handler->SendSysMessage(LANG_CHARACTER_DELETED_RESTORE);
HandleCharacterDeletedListHelper(foundList, handler);
if (newCharName.empty())
if (!newCharName)
{
// Drop nonexisting account cases
for (DeletedInfoList::iterator itr = foundList.begin(); itr != foundList.end(); ++itr)
HandleCharacterDeletedRestoreHelper(*itr, handler);
return true;
}
else if (foundList.size() == 1 && normalizePlayerName(newCharName))
if (foundList.size() == 1)
{
std::string newName{ *newCharName };
DeletedInfo delInfo = foundList.front();
// update name
delInfo.name = newCharName;
delInfo.name = newName;
// if new account provided update deleted info
if (newAccount && newAccount != delInfo.accountId)
if (newAccount)
{
delInfo.accountId = newAccount;
AccountMgr::GetName(newAccount, delInfo.accountName);
delInfo.accountId = newAccount->GetID();
delInfo.accountName = newAccount->GetName();
}
HandleCharacterDeletedRestoreHelper(delInfo, handler);
return true;
}
else
handler->SendSysMessage(LANG_CHARACTER_DELETED_ERR_RENAME);
return true;
handler->SendSysMessage(LANG_CHARACTER_DELETED_ERR_RENAME);
handler->SetSentErrorMessage(true);
return false;
}
/**
@@ -780,19 +662,16 @@ public:
*
* @param args the search string which either contains a player GUID or a part fo the character-name
*/
static bool HandleCharacterDeletedDeleteCommand(ChatHandler* handler, char const* args)
static bool HandleCharacterDeletedDeleteCommand(ChatHandler* handler, std::string needle)
{
// It is required to submit at least one argument
if (!*args)
return false;
DeletedInfoList foundList;
if (!GetDeletedCharacterInfoList(foundList, args))
if (!GetDeletedCharacterInfoList(foundList, needle))
return false;
if (foundList.empty())
{
handler->SendSysMessage(LANG_CHARACTER_DELETED_LIST_EMPTY);
handler->SetSentErrorMessage(true);
return false;
}
@@ -817,160 +696,93 @@ public:
*
* @param args the search string which either contains a player GUID or a part fo the character-name
*/
static bool HandleCharacterDeletedOldCommand(ChatHandler* /*handler*/, char const* args)
static bool HandleCharacterDeletedOldCommand(ChatHandler* /*handler*/, Optional<uint16> days)
{
int32 keepDays = sWorld->getIntConfig(CONFIG_CHARDELETE_KEEP_DAYS);
int32 keepDays = static_cast<int32>(sWorld->getIntConfig(CONFIG_CHARDELETE_KEEP_DAYS));
char* daysStr = strtok((char*)args, " ");
if (daysStr)
{
if (!isNumeric(daysStr))
return false;
keepDays = atoi(daysStr);
if (keepDays < 0)
return false;
}
// config option value 0 -> disabled and can't be used
else if (keepDays <= 0)
if (days)
keepDays = static_cast<int32>(*days);
else if (keepDays <= 0) // config option value 0 -> disabled and can't be used
return false;
Player::DeleteOldCharacters(uint32(keepDays));
Player::DeleteOldCharacters(static_cast<uint32>(keepDays));
return true;
}
static bool HandleCharacterEraseCommand(ChatHandler* handler, char const* args)
static bool HandleCharacterEraseCommand(ChatHandler* handler, PlayerIdentifier player)
{
if (!*args)
return false;
char* characterName_str = strtok((char*)args, " ");
if (!characterName_str)
return false;
std::string characterName = characterName_str;
if (!normalizePlayerName(characterName))
return false;
ObjectGuid characterGuid;
uint32 accountId;
Player* player = ObjectAccessor::FindPlayerByName(characterName);
if (player)
if (player.IsConnected())
{
characterGuid = player->GetGUID();
accountId = player->GetSession()->GetAccountId();
player->GetSession()->KickPlayer("HandleCharacterEraseCommand GM Command deleting character");
}
else
{
characterGuid = sCharacterCache->GetCharacterGuidByName(characterName);
if (!characterGuid)
{
handler->PSendSysMessage(LANG_NO_PLAYER, characterName.c_str());
handler->SetSentErrorMessage(true);
return false;
}
accountId = sCharacterCache->GetCharacterAccountIdByGuid(characterGuid);
}
accountId = sCharacterCache->GetCharacterAccountIdByGuid(player);
std::string accountName;
AccountMgr::GetName(accountId, accountName);
Player::DeleteFromDB(characterGuid, accountId, true, true);
handler->PSendSysMessage(LANG_CHARACTER_DELETED, characterName.c_str(), characterGuid.ToString().c_str(), accountName.c_str(), accountId);
Player::DeleteFromDB(player, accountId, true, true);
handler->PSendSysMessage(LANG_CHARACTER_DELETED, player.GetName().c_str(), player.GetGUID().ToString().c_str(), accountName.c_str(), accountId);
return true;
}
static bool HandleLevelUpCommand(ChatHandler* handler, char const* args)
static bool HandleLevelUpCommand(ChatHandler* handler, Optional<PlayerIdentifier> player, Optional<int16> level)
{
char* nameStr;
char* levelStr;
handler->extractOptFirstArg((char*)args, &nameStr, &levelStr);
// exception opt second arg: .character level $name
if (levelStr && isalpha(levelStr[0]))
{
nameStr = levelStr;
levelStr = nullptr; // current level will be used
}
Player* target;
ObjectGuid targetGuid;
std::string targetName;
if (!handler->extractPlayerTarget(nameStr, &target, &targetGuid, &targetName))
if (!player)
player = PlayerIdentifier::FromTargetOrSelf(handler);
if (!player)
return false;
int32 oldlevel = target ? target->GetLevel() : sCharacterCache->GetCharacterLevelByGuid(targetGuid);
int32 addlevel = levelStr ? atoi(levelStr) : 1;
int32 newlevel = oldlevel + addlevel;
uint8 oldlevel = static_cast<uint8>(player->IsConnected() ? player->GetConnectedPlayer()->GetLevel() : sCharacterCache->GetCharacterLevelByGuid(*player));
int16 newlevel = static_cast<int16>(oldlevel) + level.value_or(1);
if (newlevel < 1)
newlevel = 1;
if (newlevel > STRONG_MAX_LEVEL) // hardcoded maximum level
newlevel = STRONG_MAX_LEVEL;
if (newlevel > static_cast<int16>(STRONG_MAX_LEVEL))
newlevel = static_cast<int16>(STRONG_MAX_LEVEL);
HandleCharacterLevel(target, targetGuid, oldlevel, newlevel, handler);
if (!handler->GetSession() || handler->GetSession()->GetPlayer() != target) // including chr == NULL
if (Player* target = player->GetConnectedPlayer())
{
std::string nameLink = handler->playerLink(targetName);
handler->PSendSysMessage(LANG_YOU_CHANGE_LVL, nameLink.c_str(), newlevel);
target->GiveLevel(static_cast<uint8>(newlevel));
target->InitTalentForLevel();
target->SetXP(0);
if (handler->needReportToTarget(target))
{
if (oldlevel == newlevel)
ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_LEVEL_PROGRESS_RESET, handler->GetNameLink().c_str());
else if (oldlevel < static_cast<uint8>(newlevel))
ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_LEVEL_UP, handler->GetNameLink().c_str(), newlevel);
else // if (oldlevel > newlevel)
ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_LEVEL_DOWN, handler->GetNameLink().c_str(), newlevel);
}
}
else
{
// Update level and reset XP, everything else will be updated at login
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_LEVEL);
stmt->setUInt8(0, static_cast<uint8>(newlevel));
stmt->setUInt64(1, player->GetGUID().GetCounter());
CharacterDatabase.Execute(stmt);
}
if (!handler->GetSession() || (handler->GetSession()->GetPlayer() != player->GetConnectedPlayer())) // including chr == NULL
handler->PSendSysMessage(LANG_YOU_CHANGE_LVL, handler->playerLink(player->GetName()).c_str(), newlevel);
return true;
}
static bool HandlePDumpLoadCommand(ChatHandler* handler, char const* args)
static bool HandlePDumpLoadCommand(ChatHandler* handler, std::string fileName, AccountIdentifier account, Optional<std::string_view> characterName, Optional<ObjectGuid::LowType> characterGUID)
{
if (!*args)
return false;
char* fileStr = strtok((char*)args, " ");
if (!fileStr)
return false;
char* accountStr = strtok(nullptr, " ");
if (!accountStr)
return false;
std::string accountName = accountStr;
if (!Utf8ToUpperOnlyLatin(accountName))
{
handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
handler->SetSentErrorMessage(true);
return false;
}
uint32 accountId = AccountMgr::GetId(accountName);
if (!accountId)
{
accountId = atoi(accountStr); // use original string
if (!accountId)
{
handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
handler->SetSentErrorMessage(true);
return false;
}
}
if (!AccountMgr::GetName(accountId, accountName))
{
handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
handler->SetSentErrorMessage(true);
return false;
}
char* guidStr = nullptr;
char* nameStr = strtok(nullptr, " ");
std::string name;
if (nameStr)
if (characterName)
{
name = nameStr;
name.assign(*characterName);
// normalize the name if specified and check if it exists
if (!normalizePlayerName(name))
{
@@ -985,45 +797,33 @@ public:
handler->SetSentErrorMessage(true);
return false;
}
guidStr = strtok(nullptr, " ");
}
ObjectGuid::LowType guid = UI64LIT(0);
if (guidStr)
if (characterGUID)
{
guid = strtoull(guidStr, nullptr, 10);
if (!guid)
if (sCharacterCache->GetCharacterCacheByGuid(ObjectGuid::Create<HighGuid::Player>(*characterGUID)))
{
handler->PSendSysMessage(LANG_INVALID_CHARACTER_GUID);
handler->SetSentErrorMessage(true);
return false;
}
if (sCharacterCache->GetCharacterAccountIdByGuid(ObjectGuid::Create<HighGuid::Player>(guid)))
{
handler->PSendSysMessage(LANG_CHARACTER_GUID_IN_USE, std::to_string(guid).c_str());
handler->PSendSysMessage(LANG_CHARACTER_GUID_IN_USE, *characterGUID);
handler->SetSentErrorMessage(true);
return false;
}
}
switch (PlayerDumpReader().LoadDump(fileStr, accountId, name, guid))
switch (PlayerDumpReader().LoadDump(fileName, account, name, characterGUID.value_or(0)))
{
case DUMP_SUCCESS:
handler->PSendSysMessage(LANG_COMMAND_IMPORT_SUCCESS);
break;
case DUMP_FILE_OPEN_ERROR:
handler->PSendSysMessage(LANG_FILE_OPEN_FAIL, fileStr);
handler->PSendSysMessage(LANG_FILE_OPEN_FAIL, fileName.c_str());
handler->SetSentErrorMessage(true);
return false;
case DUMP_FILE_BROKEN:
handler->PSendSysMessage(LANG_DUMP_BROKEN, fileStr);
handler->PSendSysMessage(LANG_DUMP_BROKEN, fileName.c_str());
handler->SetSentErrorMessage(true);
return false;
case DUMP_TOO_MANY_CHARS:
handler->PSendSysMessage(LANG_ACCOUNT_CHARACTER_LIST_FULL, accountName.c_str(), accountId);
handler->PSendSysMessage(LANG_ACCOUNT_CHARACTER_LIST_FULL, account.GetName().c_str(), account.GetID());
handler->SetSentErrorMessage(true);
return false;
default:
@@ -1035,48 +835,15 @@ public:
return true;
}
static bool HandlePDumpWriteCommand(ChatHandler* handler, char const* args)
static bool HandlePDumpWriteCommand(ChatHandler* handler, std::string fileName, PlayerIdentifier player)
{
if (!*args)
return false;
char* fileStr = strtok((char*)args, " ");
char* playerStr = strtok(nullptr, " ");
if (!fileStr || !playerStr)
return false;
ObjectGuid guid;
// character name can't start from number
if (isNumeric(playerStr))
guid = ObjectGuid::Create<HighGuid::Player>(strtoull(playerStr, nullptr, 10));
else
{
std::string name = handler->extractPlayerNameFromLink(playerStr);
if (name.empty())
{
handler->SendSysMessage(LANG_PLAYER_NOT_FOUND);
handler->SetSentErrorMessage(true);
return false;
}
guid = sCharacterCache->GetCharacterGuidByName(name);
}
if (!sCharacterCache->GetCharacterAccountIdByGuid(guid))
{
handler->PSendSysMessage(LANG_PLAYER_NOT_FOUND);
handler->SetSentErrorMessage(true);
return false;
}
switch (PlayerDumpWriter().WriteDump(fileStr, guid.GetCounter()))
switch (PlayerDumpWriter().WriteDump(fileName, player.GetGUID().GetCounter()))
{
case DUMP_SUCCESS:
handler->PSendSysMessage(LANG_COMMAND_EXPORT_SUCCESS);
break;
case DUMP_FILE_OPEN_ERROR:
handler->PSendSysMessage(LANG_FILE_OPEN_FAIL, fileStr);
handler->PSendSysMessage(LANG_FILE_OPEN_FAIL, fileName.c_str());
handler->SetSentErrorMessage(true);
return false;
case DUMP_CHARACTER_DELETED: