mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
feature - (#1100) Linking of "trusted" accounts to allow altbot-control apart from own account or guild (#1267)
* Add table to store the security keys for accounts. * Add table to store relationships between accounts. * Add a new configuration option to enable or disable trusted account bots. * add checks for linked accounts * Handle account linking and chat commands * fix uppercase typo * change query & fix chatcommandtable * add missing functions to header * move account linking to updates dir * moved table creation to correct updates folder * use playerbots db instead of character db * fix db * fix install? * remove duplicated logic and add hashing to stored securityKey * add object before call * change chat variable * rename SQL file for correct execution order * add header include for ubuntu compatibility * remove old sql
This commit is contained in:
@@ -132,6 +132,9 @@ AiPlayerbot.AllowAccountBots = 1
|
||||
# Allow/deny bots in the player's guild
|
||||
AiPlayerbot.AllowGuildBots = 1
|
||||
|
||||
# Allow linking accounts for shared alt-bot control
|
||||
AiPlayerbot.AllowTrustedAccountBots = 1
|
||||
|
||||
# Random bot guild count
|
||||
AiPlayerbot.RandomBotGuildCount = 20
|
||||
|
||||
|
||||
7
data/sql/playerbots/base/playerbotds_account_keys.sql
Normal file
7
data/sql/playerbots/base/playerbotds_account_keys.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
DROP TABLE IF EXISTS `playerbot_account_keys`;
|
||||
|
||||
CREATE TABLE `playerbot_account_keys` (
|
||||
`account_id` INT PRIMARY KEY,
|
||||
`security_key` VARCHAR(255) NOT NULL,
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=INNODB DEFAULT CHARSET=latin1;
|
||||
9
data/sql/playerbots/base/playerbots_account_links.sql
Normal file
9
data/sql/playerbots/base/playerbots_account_links.sql
Normal file
@@ -0,0 +1,9 @@
|
||||
DROP TABLE IF EXISTS `playerbot_account_links`;
|
||||
|
||||
CREATE TABLE `playerbot_account_links` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`account_id` INT NOT NULL,
|
||||
`linked_account_id` INT NOT NULL,
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY `account_link` (`account_id`, `linked_account_id`)
|
||||
) ENGINE=INNODB DEFAULT CHARSET=latin1;
|
||||
@@ -0,0 +1,17 @@
|
||||
DROP TABLE IF EXISTS `playerbot_account_links`;
|
||||
|
||||
CREATE TABLE `playerbot_account_links` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`account_id` INT NOT NULL,
|
||||
`linked_account_id` INT NOT NULL,
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY `account_link` (`account_id`, `linked_account_id`)
|
||||
) ENGINE=INNODB DEFAULT CHARSET=latin1;
|
||||
|
||||
DROP TABLE IF EXISTS `playerbot_account_keys`;
|
||||
|
||||
CREATE TABLE `playerbot_account_keys` (
|
||||
`account_id` INT PRIMARY KEY,
|
||||
`security_key` VARCHAR(255) NOT NULL,
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=INNODB DEFAULT CHARSET=latin1;
|
||||
@@ -128,6 +128,7 @@ bool PlayerbotAIConfig::Initialize()
|
||||
|
||||
allowAccountBots = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowAccountBots", true);
|
||||
allowGuildBots = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowGuildBots", true);
|
||||
allowTrustedAccountBots = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowTrustedAccountBots", true);
|
||||
randomBotGuildNearby = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotGuildNearby", false);
|
||||
randomBotInvitePlayer = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotInvitePlayer", false);
|
||||
inviteChat = sConfigMgr->GetOption<bool>("AiPlayerbot.InviteChat", false);
|
||||
|
||||
@@ -55,7 +55,7 @@ public:
|
||||
bool IsInPvpProhibitedArea(uint32 id);
|
||||
|
||||
bool enabled;
|
||||
bool allowAccountBots, allowGuildBots;
|
||||
bool allowAccountBots, allowGuildBots, allowTrustedAccountBots;
|
||||
bool randomBotGuildNearby, randomBotInvitePlayer, inviteChat;
|
||||
uint32 globalCoolDown, reactDelay, maxWaitForMove, disableMoveSplinePath, maxMovementSearchTime, expireActionTime,
|
||||
dispelAuraDuration, passiveDelay, repeatDelay, errorDelay, rpgDelay, sitDelay, returnDelay, lootDelay;
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
#include <cstring>
|
||||
#include <istream>
|
||||
#include <string>
|
||||
#include <openssl/sha.h>
|
||||
#include <unordered_set>
|
||||
#include <iomanip>
|
||||
|
||||
#include "ChannelMgr.h"
|
||||
#include "CharacterCache.h"
|
||||
@@ -102,12 +104,13 @@ void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId
|
||||
Guild* guild = masterPlayer ? sGuildMgr->GetGuildById(masterPlayer->GetGuildId()) : nullptr;
|
||||
bool sameGuild = sPlayerbotAIConfig->allowGuildBots && guild && guild->GetMember(playerGuid);
|
||||
bool addClassBot = sRandomPlayerbotMgr->IsAddclassBot(playerGuid.GetCounter());
|
||||
bool linkedAccount = sPlayerbotAIConfig->allowTrustedAccountBots && IsAccountLinked(accountId, masterAccountId);
|
||||
|
||||
bool allowed = true;
|
||||
std::ostringstream out;
|
||||
std::string botName;
|
||||
sCharacterCache->GetCharacterNameByGuid(playerGuid, botName);
|
||||
if (!isRndbot && !sameAccount && !sameGuild && !addClassBot)
|
||||
if (!isRndbot && !sameAccount && !sameGuild && !addClassBot && !linkedAccount)
|
||||
{
|
||||
allowed = false;
|
||||
out << "Failure: You are not allowed to control bot " << botName.c_str();
|
||||
@@ -151,6 +154,13 @@ void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId
|
||||
{ HandlePlayerBotLoginCallback(static_cast<PlayerbotLoginQueryHolder const&>(holder)); });
|
||||
}
|
||||
|
||||
bool PlayerbotHolder::IsAccountLinked(uint32 accountId, uint32 linkedAccountId)
|
||||
{
|
||||
QueryResult result = PlayerbotsDatabase.Query(
|
||||
"SELECT 1 FROM playerbot_account_links WHERE account_id = {} AND linked_account_id = {}", accountId, linkedAccountId);
|
||||
return result != nullptr;
|
||||
}
|
||||
|
||||
void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder const& holder)
|
||||
{
|
||||
uint32 botAccountId = holder.GetAccountId();
|
||||
@@ -674,8 +684,11 @@ std::string const PlayerbotHolder::ProcessBotCommand(std::string const cmd, Obje
|
||||
if (!accountId)
|
||||
return "character not found";
|
||||
|
||||
if (!sPlayerbotAIConfig->allowAccountBots && accountId != masterAccountId)
|
||||
return "you can only add bots from your own account";
|
||||
if (!sPlayerbotAIConfig->allowAccountBots && accountId != masterAccountId &&
|
||||
!(sPlayerbotAIConfig->allowTrustedAccountBots && IsAccountLinked(accountId, masterAccountId)))
|
||||
{
|
||||
return "you can only add bots from your own account or linked accounts";
|
||||
}
|
||||
}
|
||||
|
||||
AddPlayerBot(guid, masterAccountId);
|
||||
@@ -1705,3 +1718,121 @@ PlayerbotMgr* PlayerbotsMgr::GetPlayerbotMgr(Player* player)
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void PlayerbotMgr::HandleSetSecurityKeyCommand(Player* player, const std::string& key)
|
||||
{
|
||||
uint32 accountId = player->GetSession()->GetAccountId();
|
||||
|
||||
// Hash the security key using SHA-256
|
||||
unsigned char hash[SHA256_DIGEST_LENGTH];
|
||||
SHA256((unsigned char*)key.c_str(), key.size(), hash);
|
||||
|
||||
// Convert the hash to a hexadecimal string
|
||||
std::ostringstream hashedKey;
|
||||
for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i)
|
||||
hashedKey << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
|
||||
|
||||
// Store the hashed key in the database
|
||||
PlayerbotsDatabase.Execute(
|
||||
"REPLACE INTO playerbot_account_keys (account_id, security_key) VALUES ({}, '{}')",
|
||||
accountId, hashedKey.str());
|
||||
|
||||
ChatHandler(player->GetSession()).PSendSysMessage("Security key set successfully.");
|
||||
}
|
||||
|
||||
void PlayerbotMgr::HandleLinkAccountCommand(Player* player, const std::string& accountName, const std::string& key)
|
||||
{
|
||||
QueryResult result = LoginDatabase.Query("SELECT id FROM account WHERE username = '{}'", accountName);
|
||||
if (!result)
|
||||
{
|
||||
ChatHandler(player->GetSession()).PSendSysMessage("Account not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
Field* fields = result->Fetch();
|
||||
uint32 linkedAccountId = fields[0].Get<uint32>();
|
||||
|
||||
result = PlayerbotsDatabase.Query("SELECT security_key FROM playerbot_account_keys WHERE account_id = {}", linkedAccountId);
|
||||
if (!result)
|
||||
{
|
||||
ChatHandler(player->GetSession()).PSendSysMessage("Invalid security key.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Hash the provided key
|
||||
unsigned char hash[SHA256_DIGEST_LENGTH];
|
||||
SHA256((unsigned char*)key.c_str(), key.size(), hash);
|
||||
|
||||
// Convert the hash to a hexadecimal string
|
||||
std::ostringstream hashedKey;
|
||||
for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i)
|
||||
hashedKey << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
|
||||
|
||||
// Compare the hashed key with the stored hashed key
|
||||
std::string storedKey = result->Fetch()->Get<std::string>();
|
||||
if (hashedKey.str() != storedKey)
|
||||
{
|
||||
ChatHandler(player->GetSession()).PSendSysMessage("Invalid security key.");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 accountId = player->GetSession()->GetAccountId();
|
||||
PlayerbotsDatabase.Execute(
|
||||
"INSERT IGNORE INTO playerbot_account_links (account_id, linked_account_id) VALUES ({}, {})",
|
||||
accountId, linkedAccountId);
|
||||
PlayerbotsDatabase.Execute(
|
||||
"INSERT IGNORE INTO playerbot_account_links (account_id, linked_account_id) VALUES ({}, {})",
|
||||
linkedAccountId, accountId);
|
||||
|
||||
ChatHandler(player->GetSession()).PSendSysMessage("Account linked successfully.");
|
||||
}
|
||||
|
||||
void PlayerbotMgr::HandleViewLinkedAccountsCommand(Player* player)
|
||||
{
|
||||
uint32 accountId = player->GetSession()->GetAccountId();
|
||||
QueryResult result = PlayerbotsDatabase.Query("SELECT linked_account_id FROM playerbot_account_links WHERE account_id = {}", accountId);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
ChatHandler(player->GetSession()).PSendSysMessage("No linked accounts.");
|
||||
return;
|
||||
}
|
||||
|
||||
ChatHandler(player->GetSession()).PSendSysMessage("Linked accounts:");
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
uint32 linkedAccountId = fields[0].Get<uint32>();
|
||||
|
||||
QueryResult accountResult = LoginDatabase.Query("SELECT username FROM account WHERE id = {}", linkedAccountId);
|
||||
if (accountResult)
|
||||
{
|
||||
Field* accountFields = accountResult->Fetch();
|
||||
std::string username = accountFields[0].Get<std::string>();
|
||||
ChatHandler(player->GetSession()).PSendSysMessage("- {}", username.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
ChatHandler(player->GetSession()).PSendSysMessage("- Unknown account");
|
||||
}
|
||||
} while (result->NextRow());
|
||||
}
|
||||
|
||||
void PlayerbotMgr::HandleUnlinkAccountCommand(Player* player, const std::string& accountName)
|
||||
{
|
||||
QueryResult result = LoginDatabase.Query("SELECT id FROM account WHERE username = '{}'", accountName);
|
||||
if (!result)
|
||||
{
|
||||
ChatHandler(player->GetSession()).PSendSysMessage("Account not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
Field* fields = result->Fetch();
|
||||
uint32 linkedAccountId = fields[0].Get<uint32>();
|
||||
uint32 accountId = player->GetSession()->GetAccountId();
|
||||
|
||||
PlayerbotsDatabase.Execute("DELETE FROM playerbot_account_links WHERE (account_id = {} AND linked_account_id = {}) OR (account_id = {} AND linked_account_id = {})",
|
||||
accountId, linkedAccountId, linkedAccountId, accountId);
|
||||
|
||||
ChatHandler(player->GetSession()).PSendSysMessage("Account unlinked successfully.");
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ public:
|
||||
virtual ~PlayerbotHolder(){};
|
||||
|
||||
void AddPlayerBot(ObjectGuid guid, uint32 masterAccountId);
|
||||
bool IsAccountLinked(uint32 accountId, uint32 masterAccountId);
|
||||
void HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder const& holder);
|
||||
|
||||
void LogoutPlayerBot(ObjectGuid guid);
|
||||
@@ -82,6 +83,11 @@ public:
|
||||
|
||||
void SaveToDB();
|
||||
|
||||
void HandleSetSecurityKeyCommand(Player* player, const std::string& key);
|
||||
void HandleLinkAccountCommand(Player* player, const std::string& accountName, const std::string& key);
|
||||
void HandleViewLinkedAccountsCommand(Player* player);
|
||||
void HandleUnlinkAccountCommand(Player* player, const std::string& accountName);
|
||||
|
||||
protected:
|
||||
void OnBotLoginInternal(Player* const bot) override;
|
||||
void CheckTellErrors(uint32 elapsed);
|
||||
|
||||
@@ -33,12 +33,21 @@ public:
|
||||
static ChatCommandTable playerbotsDebugCommandTable = {
|
||||
{"bg", HandleDebugBGCommand, SEC_GAMEMASTER, Console::Yes},
|
||||
};
|
||||
|
||||
static ChatCommandTable playerbotsAccountCommandTable = {
|
||||
{"setKey", HandleSetSecurityKeyCommand, SEC_PLAYER, Console::No},
|
||||
{"link", HandleLinkAccountCommand, SEC_PLAYER, Console::No},
|
||||
{"linkedAccounts", HandleViewLinkedAccountsCommand, SEC_PLAYER, Console::No},
|
||||
{"unlink", HandleUnlinkAccountCommand, SEC_PLAYER, Console::No},
|
||||
};
|
||||
|
||||
static ChatCommandTable playerbotsCommandTable = {
|
||||
{"bot", HandlePlayerbotCommand, SEC_PLAYER, Console::No},
|
||||
{"gtask", HandleGuildTaskCommand, SEC_GAMEMASTER, Console::Yes},
|
||||
{"pmon", HandlePerfMonCommand, SEC_GAMEMASTER, Console::Yes},
|
||||
{"rndbot", HandleRandomPlayerbotCommand, SEC_GAMEMASTER, Console::Yes},
|
||||
{"debug", playerbotsDebugCommandTable},
|
||||
{"account", playerbotsAccountCommandTable},
|
||||
};
|
||||
|
||||
static ChatCommandTable commandTable = {
|
||||
@@ -101,6 +110,103 @@ public:
|
||||
{
|
||||
return BGTactics::HandleConsoleCommand(handler, args);
|
||||
}
|
||||
|
||||
static bool HandleSetSecurityKeyCommand(ChatHandler* handler, char const* args)
|
||||
{
|
||||
if (!args || !*args)
|
||||
{
|
||||
handler->PSendSysMessage("Usage: .playerbots account setKey <securityKey>");
|
||||
return false;
|
||||
}
|
||||
|
||||
Player* player = handler->GetSession()->GetPlayer();
|
||||
std::string key = args;
|
||||
|
||||
PlayerbotMgr* mgr = sPlayerbotsMgr->GetPlayerbotMgr(player);
|
||||
if (mgr)
|
||||
{
|
||||
mgr->HandleSetSecurityKeyCommand(player, key);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
handler->PSendSysMessage("PlayerbotMgr instance not found.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool HandleLinkAccountCommand(ChatHandler* handler, char const* args)
|
||||
{
|
||||
if (!args || !*args)
|
||||
return false;
|
||||
|
||||
char* accountName = strtok((char*)args, " ");
|
||||
char* key = strtok(nullptr, " ");
|
||||
|
||||
if (!accountName || !key)
|
||||
{
|
||||
handler->PSendSysMessage("Usage: .playerbots account link <accountName> <securityKey>");
|
||||
return false;
|
||||
}
|
||||
|
||||
Player* player = handler->GetSession()->GetPlayer();
|
||||
|
||||
PlayerbotMgr* mgr = sPlayerbotsMgr->GetPlayerbotMgr(player);
|
||||
if (mgr)
|
||||
{
|
||||
mgr->HandleLinkAccountCommand(player, accountName, key);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
handler->PSendSysMessage("PlayerbotMgr instance not found.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool HandleViewLinkedAccountsCommand(ChatHandler* handler, char const* /*args*/)
|
||||
{
|
||||
Player* player = handler->GetSession()->GetPlayer();
|
||||
|
||||
PlayerbotMgr* mgr = sPlayerbotsMgr->GetPlayerbotMgr(player);
|
||||
if (mgr)
|
||||
{
|
||||
mgr->HandleViewLinkedAccountsCommand(player);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
handler->PSendSysMessage("PlayerbotMgr instance not found.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool HandleUnlinkAccountCommand(ChatHandler* handler, char const* args)
|
||||
{
|
||||
if (!args || !*args)
|
||||
return false;
|
||||
|
||||
char* accountName = strtok((char*)args, " ");
|
||||
if (!accountName)
|
||||
{
|
||||
handler->PSendSysMessage("Usage: .playerbots account unlink <accountName>");
|
||||
return false;
|
||||
}
|
||||
|
||||
Player* player = handler->GetSession()->GetPlayer();
|
||||
|
||||
PlayerbotMgr* mgr = sPlayerbotsMgr->GetPlayerbotMgr(player);
|
||||
if (mgr)
|
||||
{
|
||||
mgr->HandleUnlinkAccountCommand(player, accountName);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
handler->PSendSysMessage("PlayerbotMgr instance not found.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_playerbots_commandscript() { new playerbots_commandscript(); }
|
||||
|
||||
Reference in New Issue
Block a user