[HOT FIX] MS build issues regarding folder / command lenght usage or rc.exe (#2038)

This commit is contained in:
bashermens
2026-01-19 22:45:28 +01:00
committed by GitHub
parent fd07e02a8a
commit 41c53365ae
1119 changed files with 27 additions and 27 deletions

612
src/Bot/Cmd/ChatFilter.cpp Normal file
View File

@@ -0,0 +1,612 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "ChatFilter.h"
#include "Group.h"
#include "Playerbots.h"
#include "RtiTargetValue.h"
#include "AiFactory.h"
#include <algorithm>
#include <cctype>
#include <string>
static std::string ToLower(const std::string& str)
{
std::string out = str;
std::transform(out.begin(), out.end(), out.begin(), [](unsigned char c){ return std::tolower(c); });
return out;
}
std::string const ChatFilter::Filter(std::string& message)
{
if (message.find("@") == std::string::npos)
return message;
return message.substr(message.find(" ") + 1);
}
class StrategyChatFilter : public ChatFilter
{
public:
StrategyChatFilter(PlayerbotAI* botAI) : ChatFilter(botAI) {}
std::string const Filter(std::string& message) override
{
Player* bot = botAI->GetBot();
std::string msgLower = ToLower(message);
bool tank = msgLower.find("@tank") == 0;
if (tank && !botAI->IsTank(bot))
return "";
bool dps = msgLower.find("@dps") == 0;
if (dps && (botAI->IsTank(bot) || botAI->IsHeal(bot)))
return "";
bool heal = msgLower.find("@heal") == 0;
if (heal && !botAI->IsHeal(bot))
return "";
bool ranged = msgLower.find("@ranged") == 0;
if (ranged && !botAI->IsRanged(bot))
return "";
bool melee = msgLower.find("@melee") == 0;
if (melee && botAI->IsRanged(bot))
return "";
bool rangeddps = msgLower.find("@rangeddps") == 0;
if (rangeddps && (!botAI->IsRanged(bot) || botAI->IsTank(bot) || botAI->IsHeal(bot)))
return "";
bool meleedps = msgLower.find("@meleedps") == 0;
if (meleedps && (!botAI->IsMelee(bot) || botAI->IsTank(bot) || botAI->IsHeal(bot)))
return "";
if (tank || dps || heal || ranged || melee)
return ChatFilter::Filter(message);
return message;
}
};
class LevelChatFilter : public ChatFilter
{
public:
LevelChatFilter(PlayerbotAI* botAI) : ChatFilter(botAI) {}
std::string const Filter(std::string& message) override
{
Player* bot = botAI->GetBot();
std::string msgLower = ToLower(message);
if (msgLower[0] != '@')
return message;
if (msgLower.find("-") != std::string::npos)
{
uint32 fromLevel = atoi(message.substr(message.find("@") + 1, message.find("-")).c_str());
uint32 toLevel = atoi(message.substr(message.find("-") + 1, message.find(" ")).c_str());
if (bot->GetLevel() >= fromLevel && bot->GetLevel() <= toLevel)
return ChatFilter::Filter(message);
return message;
}
uint32 level = atoi(message.substr(message.find("@") + 1, message.find(" ")).c_str());
if (bot->GetLevel() == level)
return ChatFilter::Filter(message);
return message;
}
};
class CombatTypeChatFilter : public ChatFilter
{
public:
CombatTypeChatFilter(PlayerbotAI* botAI) : ChatFilter(botAI) {}
std::string const Filter(std::string& message) override
{
Player* bot = botAI->GetBot();
std::string msgLower = ToLower(message);
bool melee = msgLower.find("@melee") == 0;
bool ranged = msgLower.find("@ranged") == 0;
if (!melee && !ranged)
return message;
switch (bot->getClass())
{
case CLASS_WARRIOR:
case CLASS_PALADIN:
case CLASS_ROGUE:
case CLASS_DEATH_KNIGHT:
if (ranged)
return "";
break;
case CLASS_HUNTER:
case CLASS_PRIEST:
case CLASS_MAGE:
case CLASS_WARLOCK:
if (melee)
return "";
break;
case CLASS_DRUID:
if (ranged && botAI->IsTank(bot))
return "";
if (melee && !botAI->IsTank(bot))
return "";
break;
case CLASS_SHAMAN:
if (melee && botAI->IsHeal(bot))
return "";
if (ranged && !botAI->IsHeal(bot))
return "";
break;
}
return ChatFilter::Filter(message);
}
};
class RtiChatFilter : public ChatFilter
{
public:
RtiChatFilter(PlayerbotAI* botAI) : ChatFilter(botAI)
{
rtis.push_back("@star");
rtis.push_back("@circle");
rtis.push_back("@diamond");
rtis.push_back("@triangle");
rtis.push_back("@moon");
rtis.push_back("@square");
rtis.push_back("@cross");
rtis.push_back("@skull");
}
std::string const Filter(std::string& message) override
{
Player* bot = botAI->GetBot();
std::string msgLower = ToLower(message);
Group* group = bot->GetGroup();
if (!group)
return message;
bool found = false;
for (std::vector<std::string>::iterator i = rtis.begin(); i != rtis.end(); i++)
{
std::string const rti = *i;
std::string rtiLower = ToLower(rti);
bool isRti = msgLower.find(rtiLower) == 0;
if (!isRti)
continue;
ObjectGuid rtiTarget = group->GetTargetIcon(RtiTargetValue::GetRtiIndex(rti.substr(1)));
if (bot->GetGUID() == rtiTarget)
return ChatFilter::Filter(message);
Unit* target = *botAI->GetAiObjectContext()->GetValue<Unit*>("current target");
if (!target)
return "";
if (target->GetGUID() != rtiTarget)
return "";
found |= isRti;
if (found)
break;
}
if (found)
return ChatFilter::Filter(message);
return message;
}
private:
std::vector<std::string> rtis;
};
class ClassChatFilter : public ChatFilter
{
public:
ClassChatFilter(PlayerbotAI* botAI) : ChatFilter(botAI)
{
classNames["@dk"] = CLASS_DEATH_KNIGHT;
classNames["@druid"] = CLASS_DRUID;
classNames["@hunter"] = CLASS_HUNTER;
classNames["@mage"] = CLASS_MAGE;
classNames["@paladin"] = CLASS_PALADIN;
classNames["@priest"] = CLASS_PRIEST;
classNames["@rogue"] = CLASS_ROGUE;
classNames["@shaman"] = CLASS_SHAMAN;
classNames["@warlock"] = CLASS_WARLOCK;
classNames["@warrior"] = CLASS_WARRIOR;
}
std::string const Filter(std::string& message) override
{
Player* bot = botAI->GetBot();
std::string msgLower = ToLower(message);
bool found = false;
for (std::map<std::string, uint8>::iterator i = classNames.begin(); i != classNames.end(); i++)
{
bool isClass = msgLower.find(ToLower(i->first)) == 0;
if (isClass && bot->getClass() != i->second)
return "";
found |= isClass;
if (found)
break;
}
if (found)
return ChatFilter::Filter(message);
return message;
}
private:
std::map<std::string, uint8> classNames;
};
class SubGroupChatFilter : public ChatFilter
{
public:
SubGroupChatFilter(PlayerbotAI* botAI) : ChatFilter(botAI) {}
std::string const Filter(std::string& message) override
{
Player* bot = botAI->GetBot();
std::string msgLower = ToLower(message);
if (msgLower.find("@group") == 0)
{
size_t spacePos = message.find(" ");
if (spacePos == std::string::npos)
return message;
std::string pnum = message.substr(6, spacePos - 6);
std::string actualMessage = message.substr(spacePos + 1);
std::set<uint32> targets;
std::istringstream ss(pnum);
std::string token;
while (std::getline(ss, token, ','))
{
size_t dashPos = token.find("-");
if (dashPos != std::string::npos)
{
uint32 from = atoi(token.substr(0, dashPos).c_str());
uint32 to = atoi(token.substr(dashPos + 1).c_str());
if (from > to) std::swap(from, to);
for (uint32 i = from; i <= to; ++i)
targets.insert(i);
}
else
{
uint32 index = atoi(token.c_str());
targets.insert(index);
}
}
if (!bot->GetGroup())
return message;
uint32 sg = bot->GetSubGroup() + 1;
if (targets.find(sg) != targets.end())
return ChatFilter::Filter(actualMessage);
}
return message;
}
};
class SpecChatFilter : public ChatFilter
{
public:
SpecChatFilter(PlayerbotAI* botAI) : ChatFilter(botAI)
{
// Map (class, specTab) to spec+class string
specTabNames[{CLASS_PALADIN, 0}] = "hpal";
specTabNames[{CLASS_PALADIN, 1}] = "ppal";
specTabNames[{CLASS_PALADIN, 2}] = "rpal";
specTabNames[{CLASS_PRIEST, 0}] = "disc";
specTabNames[{CLASS_PRIEST, 1}] = "hpr";
specTabNames[{CLASS_PRIEST, 2}] = "spr";
specTabNames[{CLASS_MAGE, 0}] = "arc";
specTabNames[{CLASS_MAGE, 1}] = "frost";
specTabNames[{CLASS_MAGE, 2}] = "fire";
specTabNames[{CLASS_WARRIOR, 0}] = "arms";
specTabNames[{CLASS_WARRIOR, 1}] = "fury";
specTabNames[{CLASS_WARRIOR, 2}] = "pwar";
specTabNames[{CLASS_WARLOCK, 0}] = "affl";
specTabNames[{CLASS_WARLOCK, 1}] = "demo";
specTabNames[{CLASS_WARLOCK, 2}] = "dest";
specTabNames[{CLASS_SHAMAN, 0}] = "ele";
specTabNames[{CLASS_SHAMAN, 1}] = "enh";
specTabNames[{CLASS_SHAMAN, 2}] = "rsha";
specTabNames[{CLASS_DRUID, 0}] = "bal";
// See below for feral druid
specTabNames[{CLASS_DRUID, 2}] = "rdru";
specTabNames[{CLASS_HUNTER, 0}] = "bmh";
specTabNames[{CLASS_HUNTER, 1}] = "mmh";
specTabNames[{CLASS_HUNTER, 2}] = "svh";
specTabNames[{CLASS_ROGUE, 0}] = "mut";
specTabNames[{CLASS_ROGUE, 1}] = "comb";
specTabNames[{CLASS_ROGUE, 2}] = "sub";
// See below for blood death knight
specTabNames[{CLASS_DEATH_KNIGHT, 1}] = "fdk";
specTabNames[{CLASS_DEATH_KNIGHT, 2}] = "udk";
}
std::string const Filter(std::string& message) override
{
std::string msgLower = ToLower(message);
std::string specPrefix;
std::string rest;
if (!ParseSpecPrefix(message, specPrefix, rest))
{
return message;
}
Player* bot = botAI->GetBot();
if (!MatchesSpec(bot, specPrefix))
{
return "";
}
std::string result = ChatFilter::Filter(rest);
return result;
}
private:
std::map<std::pair<uint8, int>, std::string> specTabNames;
bool ParseSpecPrefix(const std::string& message, std::string& specPrefix, std::string& rest)
{
std::string msgLower = ToLower(message);
for (auto const& entry : specTabNames)
{
std::string prefix = "@" + entry.second;
if (msgLower.find(ToLower(prefix)) == 0)
{
specPrefix = entry.second;
size_t spacePos = message.find(' ');
rest = (spacePos != std::string::npos) ? message.substr(spacePos + 1) : "";
return true;
}
}
return false;
}
bool MatchesSpec(Player* bot, const std::string& specPrefix)
{
uint8 cls = bot->getClass();
int specTab = AiFactory::GetPlayerSpecTab(bot);
std::string botSpecClass;
// For druids, specTab==1 is always feral; distinguish bear/cat at runtime by role
if (cls == CLASS_DRUID && specTab == 1)
{
botSpecClass = botAI->IsTank(bot) ? "bear" : "cat";
}
// For death knights, specTab==0 is always blood; distinguish tank/dps at runtime by role
else if (cls == CLASS_DEATH_KNIGHT && specTab == 0)
{
botSpecClass = botAI->IsTank(bot) ? "bdkt" : "bdkd";
}
else
{
auto it = specTabNames.find({cls, specTab});
if (it != specTabNames.end())
botSpecClass = it->second;
}
return ToLower(botSpecClass) == ToLower(specPrefix);
}
};
class AuraChatFilter : public ChatFilter
{
public:
AuraChatFilter(PlayerbotAI* botAI) : ChatFilter(botAI) {}
std::string const Filter(std::string& message) override
{
Player* bot = botAI->GetBot();
std::string msgLower = ToLower(message);
const std::string auraPrefix = "@aura";
const std::string noAuraPrefix = "@noaura";
size_t prefixLen = 0;
bool isNoAura = false;
if (msgLower.find(auraPrefix) == 0)
{
prefixLen = auraPrefix.length();
isNoAura = false;
}
else if (msgLower.find(noAuraPrefix) == 0)
{
prefixLen = noAuraPrefix.length();
isNoAura = true;
}
else
{
return message;
}
// Trim any leading spaces after @aura or @noaura (can use space between prefix and spell ID if desired, but not required)
std::string auraIdOrName = message.substr(prefixLen);
auraIdOrName.erase(0, auraIdOrName.find_first_not_of(' '));
if (auraIdOrName.empty())
{
return message;
}
uint32 auraId = 0;
size_t spacePos = auraIdOrName.find(' ');
std::string idStr = (spacePos != std::string::npos) ? auraIdOrName.substr(0, spacePos) : auraIdOrName;
std::string rest = (spacePos != std::string::npos) ? auraIdOrName.substr(spacePos + 1) : "";
if (!idStr.empty())
{
bool isNumeric = std::all_of(idStr.begin(), idStr.end(), ::isdigit);
if (isNumeric)
{
auraId = atoi(idStr.c_str());
}
}
if (auraId == 0)
return message;
bool hasAura = bot->HasAura(auraId);
bool match = isNoAura ? !hasAura : hasAura;
std::string result = match ? ChatFilter::Filter(rest) : "";
return result;
}
};
class AggroByChatFilter : public ChatFilter
{
public:
AggroByChatFilter(PlayerbotAI* botAI) : ChatFilter(botAI) {}
std::string const Filter(std::string& message) override
{
Player* bot = botAI->GetBot();
std::string msgLower = ToLower(message);
const std::string prefix = "@aggroby";
size_t prefixLen = prefix.length();
if (msgLower.find(prefix) != 0)
{
return message;
}
// Trim any leading spaces after @aggroby (can use space between prefix and entry ID/creature name if desired, but not required)
std::string enemyStr = message.substr(prefixLen);
enemyStr.erase(0, enemyStr.find_first_not_of(' '));
if (enemyStr.empty())
{
return message;
}
// If creature name is more than one word, it must be enclosed in quotes, e.g. @aggroby "Scarlet Commander Mograine" flee
std::string rest = "";
std::string enemyName = "";
bool isName = false;
uint32 entryId = 0;
if (enemyStr[0] == '"')
{
size_t endQuote = enemyStr.find('"', 1);
if (endQuote != std::string::npos)
{
enemyName = enemyStr.substr(1, endQuote - 1);
isName = true;
size_t spacePos = enemyStr.find(' ', endQuote + 1);
if (spacePos != std::string::npos)
{
rest = enemyStr.substr(spacePos + 1);
}
else
{
rest = "";
}
}
else
{
enemyName = enemyStr.substr(1);
isName = true;
rest = "";
}
}
else
{
size_t splitPos = enemyStr.find_first_of(" ");
std::string idOrName = (splitPos != std::string::npos) ? enemyStr.substr(0, splitPos) : enemyStr;
if (splitPos != std::string::npos)
{
rest = enemyStr.substr(splitPos + 1);
}
else
{
rest = "";
}
if (!idOrName.empty())
{
bool isNumeric = std::all_of(idOrName.begin(), idOrName.end(), ::isdigit);
if (isNumeric)
{
entryId = atoi(idOrName.c_str());
}
else
{
enemyName = idOrName;
isName = true;
}
}
}
const float radius = 100.0f;
GuidVector npcs = botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest npcs")->Get();
bool match = false;
for (auto const& guid : npcs)
{
Creature* c = botAI->GetCreature(guid);
if (!c)
{
continue;
}
bool nameMatch = isName && ToLower(c->GetName()) == ToLower(enemyName);
bool idMatch = (entryId != 0) && c->GetEntry() == entryId;
if ((nameMatch || idMatch) && c->GetDistance2d(bot) <= radius)
{
Unit* victim = c->GetVictim();
if (victim && victim->GetGUID() == bot->GetGUID())
{
match = true;
break;
}
}
}
std::string result = match ? ChatFilter::Filter(rest) : "";
return result;
}
};
CompositeChatFilter::CompositeChatFilter(PlayerbotAI* botAI) : ChatFilter(botAI)
{
filters.push_back(new StrategyChatFilter(botAI));
filters.push_back(new ClassChatFilter(botAI));
filters.push_back(new RtiChatFilter(botAI));
filters.push_back(new CombatTypeChatFilter(botAI));
filters.push_back(new LevelChatFilter(botAI));
filters.push_back(new SubGroupChatFilter(botAI));
filters.push_back(new SpecChatFilter(botAI));
filters.push_back(new AuraChatFilter(botAI));
filters.push_back(new AggroByChatFilter(botAI));
}
CompositeChatFilter::~CompositeChatFilter()
{
for (std::vector<ChatFilter*>::iterator i = filters.begin(); i != filters.end(); i++)
delete (*i);
}
std::string const CompositeChatFilter::Filter(std::string& message)
{
for (auto* filter : filters)
{
message = filter->Filter(message);
if (message.empty())
break;
}
return message;
}

37
src/Bot/Cmd/ChatFilter.h Normal file
View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_CHATFILTER_H
#define _PLAYERBOT_CHATFILTER_H
#include <vector>
#include "Common.h"
#include "PlayerbotAIAware.h"
class PlayerbotAI;
class ChatFilter : public PlayerbotAIAware
{
public:
ChatFilter(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) {}
virtual ~ChatFilter() {}
virtual std::string const Filter(std::string& message);
};
class CompositeChatFilter : public ChatFilter
{
public:
CompositeChatFilter(PlayerbotAI* botAI);
virtual ~CompositeChatFilter();
std::string const Filter(std::string& message) override;
private:
std::vector<ChatFilter*> filters;
};
#endif

704
src/Bot/Cmd/ChatHelper.cpp Normal file
View File

@@ -0,0 +1,704 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "ChatHelper.h"
#include "AiFactory.h"
#include "Common.h"
#include "ItemTemplate.h"
#include "ObjectMgr.h"
#include "Playerbots.h"
#include "SpellInfo.h"
#include <regex>
std::map<std::string, uint32> ChatHelper::consumableSubClasses;
std::map<std::string, uint32> ChatHelper::tradeSubClasses;
std::map<std::string, uint32> ChatHelper::itemQualities;
std::map<std::string, uint32> ChatHelper::projectileSubClasses;
std::map<std::string, uint32> ChatHelper::slots;
std::map<std::string, uint32> ChatHelper::skills;
std::map<std::string, ChatMsg> ChatHelper::chats;
std::map<uint8, std::string> ChatHelper::classes;
std::map<uint8, std::string> ChatHelper::races;
std::map<uint8, std::map<uint8, std::string> > ChatHelper::specs;
template <class T>
static bool substrContainsInMap(std::string const searchTerm, std::map<std::string, T> searchIn)
{
for (typename std::map<std::string, T>::iterator i = searchIn.begin(); i != searchIn.end(); ++i)
{
std::string const term = i->first;
if (term.size() > 1 && searchTerm.find(term) != std::string::npos)
return true;
}
return false;
}
ChatHelper::ChatHelper(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
{
itemQualities["poor"] = ITEM_QUALITY_POOR;
itemQualities["gray"] = ITEM_QUALITY_POOR;
itemQualities["normal"] = ITEM_QUALITY_NORMAL;
itemQualities["white"] = ITEM_QUALITY_NORMAL;
itemQualities["uncommon"] = ITEM_QUALITY_UNCOMMON;
itemQualities["green"] = ITEM_QUALITY_UNCOMMON;
itemQualities["rare"] = ITEM_QUALITY_RARE;
itemQualities["blue"] = ITEM_QUALITY_RARE;
itemQualities["epic"] = ITEM_QUALITY_EPIC;
itemQualities["violet"] = ITEM_QUALITY_EPIC;
itemQualities["legendary"] = ITEM_QUALITY_LEGENDARY;
itemQualities["yellow"] = ITEM_QUALITY_LEGENDARY;
consumableSubClasses["potion"] = ITEM_SUBCLASS_POTION;
consumableSubClasses["elixir"] = ITEM_SUBCLASS_ELIXIR;
consumableSubClasses["flask"] = ITEM_SUBCLASS_FLASK;
consumableSubClasses["scroll"] = ITEM_SUBCLASS_SCROLL;
consumableSubClasses["food"] = ITEM_SUBCLASS_FOOD;
consumableSubClasses["bandage"] = ITEM_SUBCLASS_BANDAGE;
consumableSubClasses["enchant"] = ITEM_SUBCLASS_CONSUMABLE_OTHER;
projectileSubClasses["arrows"] = ITEM_SUBCLASS_ARROW;
projectileSubClasses["bullets"] = ITEM_SUBCLASS_BULLET;
// tradeSubClasses["cloth"] = ITEM_SUBCLASS_CLOTH;
// tradeSubClasses["leather"] = ITEM_SUBCLASS_LEATHER;
// tradeSubClasses["metal"] = ITEM_SUBCLASS_METAL_STONE;
// tradeSubClasses["stone"] = ITEM_SUBCLASS_METAL_STONE;
// tradeSubClasses["ore"] = ITEM_SUBCLASS_METAL_STONE;
// tradeSubClasses["meat"] = ITEM_SUBCLASS_MEAT;
// tradeSubClasses["herb"] = ITEM_SUBCLASS_HERB;
// tradeSubClasses["elemental"] = ITEM_SUBCLASS_ELEMENTAL;
// tradeSubClasses["disenchants"] = ITEM_SUBCLASS_ENCHANTING;
// tradeSubClasses["enchanting"] = ITEM_SUBCLASS_ENCHANTING;
// tradeSubClasses["gems"] = ITEM_SUBCLASS_JEWELCRAFTING;
// tradeSubClasses["jewels"] = ITEM_SUBCLASS_JEWELCRAFTING;
// tradeSubClasses["jewelcrafting"] = ITEM_SUBCLASS_JEWELCRAFTING;
slots["head"] = EQUIPMENT_SLOT_HEAD;
slots["neck"] = EQUIPMENT_SLOT_NECK;
slots["shoulder"] = EQUIPMENT_SLOT_SHOULDERS;
slots["shirt"] = EQUIPMENT_SLOT_BODY;
slots["chest"] = EQUIPMENT_SLOT_CHEST;
slots["waist"] = EQUIPMENT_SLOT_WAIST;
slots["legs"] = EQUIPMENT_SLOT_LEGS;
slots["feet"] = EQUIPMENT_SLOT_FEET;
slots["wrist"] = EQUIPMENT_SLOT_WRISTS;
slots["hands"] = EQUIPMENT_SLOT_HANDS;
slots["finger 1"] = EQUIPMENT_SLOT_FINGER1;
slots["finger 2"] = EQUIPMENT_SLOT_FINGER2;
slots["trinket 1"] = EQUIPMENT_SLOT_TRINKET1;
slots["trinket 2"] = EQUIPMENT_SLOT_TRINKET2;
slots["back"] = EQUIPMENT_SLOT_BACK;
slots["main hand"] = EQUIPMENT_SLOT_MAINHAND;
slots["off hand"] = EQUIPMENT_SLOT_OFFHAND;
slots["ranged"] = EQUIPMENT_SLOT_RANGED;
slots["tabard"] = EQUIPMENT_SLOT_TABARD;
skills["first aid"] = SKILL_FIRST_AID;
skills["fishing"] = SKILL_FISHING;
skills["cooking"] = SKILL_COOKING;
skills["alchemy"] = SKILL_ALCHEMY;
skills["enchanting"] = SKILL_ENCHANTING;
skills["engineering"] = SKILL_ENGINEERING;
skills["leatherworking"] = SKILL_LEATHERWORKING;
skills["blacksmithing"] = SKILL_BLACKSMITHING;
skills["tailoring"] = SKILL_TAILORING;
skills["herbalism"] = SKILL_HERBALISM;
skills["mining"] = SKILL_MINING;
skills["skinning"] = SKILL_SKINNING;
skills["jewelcrafting"] = SKILL_JEWELCRAFTING;
chats["party"] = CHAT_MSG_PARTY;
chats["p"] = CHAT_MSG_PARTY;
chats["guild"] = CHAT_MSG_GUILD;
chats["g"] = CHAT_MSG_GUILD;
chats["raid"] = CHAT_MSG_RAID;
chats["r"] = CHAT_MSG_RAID;
chats["whisper"] = CHAT_MSG_WHISPER;
chats["w"] = CHAT_MSG_WHISPER;
classes[CLASS_DRUID] = "druid";
specs[CLASS_DRUID][0] = "balance";
specs[CLASS_DRUID][1] = "feral combat";
specs[CLASS_DRUID][2] = "restoration";
classes[CLASS_HUNTER] = "hunter";
specs[CLASS_HUNTER][0] = "beast mastery";
specs[CLASS_HUNTER][1] = "marksmanship";
specs[CLASS_HUNTER][2] = "survival";
classes[CLASS_MAGE] = "mage";
specs[CLASS_MAGE][0] = "arcane";
specs[CLASS_MAGE][1] = "fire";
specs[CLASS_MAGE][2] = "frost";
classes[CLASS_PALADIN] = "paladin";
specs[CLASS_PALADIN][0] = "holy";
specs[CLASS_PALADIN][1] = "protection";
specs[CLASS_PALADIN][2] = "retribution";
classes[CLASS_PRIEST] = "priest";
specs[CLASS_PRIEST][0] = "discipline";
specs[CLASS_PRIEST][1] = "holy";
specs[CLASS_PRIEST][2] = "shadow";
classes[CLASS_ROGUE] = "rogue";
specs[CLASS_ROGUE][0] = "assasination";
specs[CLASS_ROGUE][1] = "combat";
specs[CLASS_ROGUE][2] = "subtlety";
classes[CLASS_SHAMAN] = "shaman";
specs[CLASS_SHAMAN][0] = "elemental";
specs[CLASS_SHAMAN][1] = "enhancement";
specs[CLASS_SHAMAN][2] = "restoration";
classes[CLASS_WARLOCK] = "warlock";
specs[CLASS_WARLOCK][0] = "affliction";
specs[CLASS_WARLOCK][1] = "demonology";
specs[CLASS_WARLOCK][2] = "destruction";
classes[CLASS_WARRIOR] = "warrior";
specs[CLASS_WARRIOR][0] = "arms";
specs[CLASS_WARRIOR][1] = "fury";
specs[CLASS_WARRIOR][2] = "protection";
classes[CLASS_DEATH_KNIGHT] = "dk";
specs[CLASS_DEATH_KNIGHT][0] = "blood";
specs[CLASS_DEATH_KNIGHT][1] = "frost";
specs[CLASS_DEATH_KNIGHT][2] = "unholy";
races[RACE_DWARF] = "Dwarf";
races[RACE_GNOME] = "Gnome";
races[RACE_HUMAN] = "Human";
races[RACE_NIGHTELF] = "Night Elf";
races[RACE_ORC] = "Orc";
races[RACE_TAUREN] = "Tauren";
races[RACE_TROLL] = "Troll";
races[RACE_UNDEAD_PLAYER] = "Undead";
races[RACE_BLOODELF] = "Blood Elf";
races[RACE_DRAENEI] = "Draenei";
}
std::string const ChatHelper::formatMoney(uint32 copper)
{
std::ostringstream out;
if (!copper)
{
out << "0";
return out.str();
}
uint32 gold = uint32(copper / 10000);
copper -= (gold * 10000);
uint32 silver = uint32(copper / 100);
copper -= (silver * 100);
bool space = false;
if (gold > 0)
{
out << gold << "g";
space = true;
}
if (silver > 0 && gold < 50)
{
if (space)
out << " ";
out << silver << "s";
space = true;
}
if (copper > 0 && gold < 10)
{
if (space)
out << " ";
out << copper << "c";
}
return out.str();
}
std::string ChatHelper::parseValue(const std::string& type, const std::string& text)
{
std::string retString;
std::string pattern = "Hvalue:" + type + ":";
int pos = text.find(pattern, 0);
if (pos == -1)
return retString;
pos += pattern.size();
int endPos = text.find('|', pos);
if (endPos == -1)
return retString;
retString = text.substr(pos, endPos - pos);
return retString;
}
uint32 ChatHelper::parseMoney(std::string const text)
{
// if user specified money in ##g##s##c format
std::string acum = "";
uint32 copper = 0;
for (uint8 i = 0; i < text.length(); i++)
{
if (text[i] == 'g')
{
copper += (atol(acum.c_str()) * 100 * 100);
acum = "";
}
else if (text[i] == 'c')
{
copper += atol(acum.c_str());
acum = "";
}
else if (text[i] == 's')
{
copper += (atol(acum.c_str()) * 100);
acum = "";
}
else if (text[i] == ' ')
break;
else if (text[i] >= 48 && text[i] <= 57)
acum += text[i];
else
{
copper = 0;
break;
}
}
return copper;
}
ItemIds ChatHelper::parseItems(std::string const text)
{
ItemIds itemIds;
uint8 pos = 0;
while (true)
{
auto i = text.find("Hitem:", pos);
if (i == std::string::npos)
break;
pos = i + 6;
auto endPos = text.find(':', pos);
if (endPos == std::string::npos)
break;
std::string const idC = text.substr(pos, endPos - pos);
auto id = atol(idC.c_str());
pos = endPos;
if (id)
itemIds.insert(id);
}
return itemIds;
}
ItemWithRandomProperty ChatHelper::parseItemWithRandomProperty(std::string const text)
{
ItemWithRandomProperty res;
size_t itemStart = text.find("Hitem:");
if (itemStart == std::string::npos)
return res;
itemStart += 6;
if (itemStart >= text.length())
return res;
size_t colonPos = text.find(':', itemStart);
if (colonPos == std::string::npos)
return res;
std::string itemIdStr = text.substr(itemStart, colonPos - itemStart);
res.itemId = atoi(itemIdStr.c_str());
std::vector<std::string> params;
size_t currentPos = colonPos + 1;
while (currentPos < text.length()) {
size_t nextColon = text.find(':', currentPos);
if (nextColon == std::string::npos)
{
size_t hTag = text.find("|h", currentPos);
if (hTag != std::string::npos)
{
params.push_back(text.substr(currentPos, hTag - currentPos));
}
break;
}
params.push_back(text.substr(currentPos, nextColon - currentPos));
currentPos = nextColon + 1;
}
if (params.size() >= 6)
{
res.randomPropertyId = atoi(params[5].c_str());
}
return res;
}
std::string const ChatHelper::FormatQuest(Quest const* quest)
{
if (!quest)
{
return "Invalid quest";
}
std::ostringstream out;
QuestLocale const* locale = sObjectMgr->GetQuestLocale(quest->GetQuestId());
std::string questTitle;
if (locale && locale->Title.size() > sWorld->GetDefaultDbcLocale())
questTitle = locale->Title[sWorld->GetDefaultDbcLocale()];
if (questTitle.empty())
questTitle = quest->GetTitle();
out << "|cFFFFFF00|Hquest:" << quest->GetQuestId() << ':' << quest->GetQuestLevel() << "|h[" << questTitle << "]|h|r";
return out.str();
}
std::string const ChatHelper::FormatGameobject(GameObject* go)
{
std::ostringstream out;
out << "|cFFFFFF00|Hfound:" << go->GetGUID().GetRawValue() << ":" << go->GetEntry() << ":"
<< "|h[" << go->GetNameForLocaleIdx(sWorld->GetDefaultDbcLocale()) << "]|h|r";
return out.str();
}
std::string const ChatHelper::FormatWorldobject(WorldObject* wo)
{
std::ostringstream out;
out << "|cFFFFFF00|Hfound:" << wo->GetGUID().GetRawValue() << ":" << wo->GetEntry() << ":"
<< "|h[";
out << (wo->ToGameObject() ? ((GameObject*)wo)->GetNameForLocaleIdx(sWorld->GetDefaultDbcLocale())
: wo->GetNameForLocaleIdx(sWorld->GetDefaultDbcLocale()))
<< "]|h|r";
return out.str();
}
std::string const ChatHelper::FormatWorldEntry(int32 entry)
{
CreatureTemplate const* cInfo = nullptr;
GameObjectTemplate const* gInfo = nullptr;
if (entry > 0)
cInfo = sObjectMgr->GetCreatureTemplate(entry);
else
gInfo = sObjectMgr->GetGameObjectTemplate(entry * -1);
std::ostringstream out;
out << "|cFFFFFF00|Hentry:" << abs(entry) << ":"
<< "|h[";
if (entry < 0 && gInfo)
out << gInfo->name;
else if (entry > 0 && cInfo)
out << cInfo->Name;
else
out << "unknown";
out << "]|h|r";
return out.str();
}
std::string const ChatHelper::FormatSpell(SpellInfo const* spellInfo)
{
std::ostringstream out;
std::string spellName = spellInfo->SpellName[sWorld->GetDefaultDbcLocale()] ?
spellInfo->SpellName[sWorld->GetDefaultDbcLocale()] : spellInfo->SpellName[LOCALE_enUS];
out << "|cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellName << "]|h|r";
return out.str();
}
std::string const ChatHelper::FormatItem(ItemTemplate const* proto, uint32 count, uint32 total)
{
char color[32];
snprintf(color, sizeof(color), "%x", ItemQualityColors[proto->Quality]);
std::string itemName;
const ItemLocale* locale = sObjectMgr->GetItemLocale(proto->ItemId);
if (locale && locale->Name.size() > sWorld->GetDefaultDbcLocale())
itemName = locale->Name[sWorld->GetDefaultDbcLocale()];
if (itemName.empty())
itemName = proto->Name1;
std::ostringstream out;
out << "|c" << color << "|Hitem:" << proto->ItemId << ":0:0:0:0:0:0:0"
<< "|h[" << itemName << "]|h|r";
if (count > 1)
out << "x" << count;
if (total > 0)
out << " (" << total << ")";
return out.str();
}
std::string const ChatHelper::FormatQItem(uint32 itemId)
{
char color[32];
snprintf(color, sizeof(color), "%x", ItemQualityColors[0]);
std::ostringstream out;
out << "|c" << color << "|Hitem:" << itemId << ":0:0:0:0:0:0:0"
<< "|h[item"
<< "]|h|r";
return out.str();
}
ChatMsg ChatHelper::parseChat(std::string const text)
{
if (chats.find(text) != chats.end())
return chats[text];
return CHAT_MSG_SYSTEM;
}
std::string const ChatHelper::FormatChat(ChatMsg chat)
{
switch (chat)
{
case CHAT_MSG_GUILD:
return "guild";
case CHAT_MSG_PARTY:
return "party";
case CHAT_MSG_WHISPER:
return "whisper";
case CHAT_MSG_RAID:
return "raid";
default:
break;
}
return "unknown";
}
uint32 ChatHelper::parseSpell(std::string const text)
{
PlayerbotChatHandler handler(botAI->GetBot());
return handler.extractSpellId(text);
}
GuidVector ChatHelper::parseGameobjects(std::string const text)
{
GuidVector gos;
// Link format
// |cFFFFFF00|Hfound:" << guid << ':' << entry << ':' << "|h[" << gInfo->name << "]|h|r";
// |cFFFFFF00|Hfound:9582:1731|h[Copper Vein]|h|r
uint8 pos = 0;
while (true)
{
// extract GO guid
auto i = text.find("Hfound:", pos); // base H = 11
if (i == std::string::npos) // break if error
break;
pos = i + 7; // start of window in text 11 + 7 = 18
auto endPos = text.find(':', pos); // end of window in text 22
if (endPos == std::string::npos) // break if error
break;
std::istringstream stream(text.substr(pos, endPos - pos));
uint64 guid;
stream >> guid;
// extract GO entry
pos = endPos + 1;
endPos = text.find(':', pos); // end of window in text
if (endPos == std::string::npos) // break if error
break;
std::string const entryC = text.substr(pos, endPos - pos); // get std::string const within window i.e entry
//uint32 entry = atol(entryC.c_str()); // convert ascii to float
ObjectGuid lootCurrent = ObjectGuid(guid);
if (guid)
gos.push_back(lootCurrent);
}
return gos;
}
std::string const ChatHelper::FormatQuestObjective(std::string const name, uint32 available, uint32 required)
{
std::ostringstream out;
out << "|cFFFFFFFF" << name << (available >= required ? "|c0000FF00: " : "|c00FF0000: ") << available << "/"
<< required << "|r";
return out.str();
}
uint32 ChatHelper::parseItemQuality(std::string const text)
{
if (itemQualities.find(text) == itemQualities.end())
return MAX_ITEM_QUALITY;
return itemQualities[text];
}
bool ChatHelper::parseItemClass(std::string const text, uint32* itemClass, uint32* itemSubClass)
{
if (text == "questitem")
{
*itemClass = ITEM_CLASS_QUEST;
*itemSubClass = ITEM_SUBCLASS_QUEST;
return true;
}
if (consumableSubClasses.find(text) != consumableSubClasses.end())
{
*itemClass = ITEM_CLASS_CONSUMABLE;
*itemSubClass = consumableSubClasses[text];
return true;
}
if (tradeSubClasses.find(text) != tradeSubClasses.end())
{
*itemClass = ITEM_CLASS_TRADE_GOODS;
*itemSubClass = tradeSubClasses[text];
return true;
}
if (projectileSubClasses.find(text) != projectileSubClasses.end())
{
*itemClass = ITEM_CLASS_PROJECTILE;
*itemSubClass = projectileSubClasses[text];
return true;
}
return false;
}
uint32 ChatHelper::parseSlot(std::string const text)
{
if (slots.find(text) != slots.end())
return slots[text];
return EQUIPMENT_SLOT_END;
}
bool ChatHelper::parseable(std::string const text)
{
return text.find("|H") != std::string::npos || text == "questitem" || text == "ammo" ||
substrContainsInMap<uint32>(text, consumableSubClasses) ||
substrContainsInMap<uint32>(text, tradeSubClasses) || substrContainsInMap<uint32>(text, itemQualities) ||
substrContainsInMap<uint32>(text, slots) || substrContainsInMap<ChatMsg>(text, chats) ||
substrContainsInMap<uint32>(text, skills) || parseMoney(text) > 0;
}
std::string const ChatHelper::FormatClass(Player* player, int8 spec)
{
uint8 cls = player->getClass();
std::ostringstream out;
out << specs[cls][spec] << " (";
std::map<uint8, uint32> tabs = AiFactory::GetPlayerSpecTabs(player);
uint32 c0 = tabs[0];
uint32 c1 = tabs[1];
uint32 c2 = tabs[2];
out << (c0 ? "|h|cff00ff00" : "") << c0 << "|h|cffffffff/";
out << (c1 ? "|h|cff00ff00" : "") << c1 << "|h|cffffffff/";
out << (c2 ? "|h|cff00ff00" : "") << c2 << "|h|cffffffff";
out << ")|r " << classes[cls];
return out.str();
}
std::string const ChatHelper::FormatClass(uint8 cls) { return classes[cls]; }
std::string const ChatHelper::FormatRace(uint8 race) { return races[race]; }
uint32 ChatHelper::parseSkill(std::string const text)
{
if (skills.find(text) != skills.end())
return skills[text];
return SKILL_NONE;
}
std::string const ChatHelper::FormatSkill(uint32 skill)
{
for (std::map<std::string, uint32>::iterator i = skills.begin(); i != skills.end(); ++i)
{
if (i->second == skill)
return i->first;
}
return "";
}
std::string const ChatHelper::FormatBoolean(bool flag) { return flag ? "|cff00ff00ON|r" : "|cffffff00OFF|r"; }
void ChatHelper::eraseAllSubStr(std::string& mainStr, std::string const toErase)
{
size_t pos = std::string::npos;
// Search for the substring in std::string const in a loop untill nothing is found
while ((pos = mainStr.find(toErase)) != std::string::npos)
{
// If found then erase it from std::string
mainStr.erase(pos, toErase.length());
}
}
std::set<uint32> extractGeneric(std::string_view text, std::string_view prefix)
{
std::set<uint32_t> ids;
std::string_view text_view = text;
size_t pos = 0;
while ((pos = text_view.find(prefix, pos)) != std::string::npos)
{
// skip "Hquest:/Hitem:"
pos += prefix.size();
// extract everything after "Hquest:/Hitem:"
size_t end_pos = text_view.find_first_not_of("0123456789", pos);
std::string_view number_str = text_view.substr(pos, end_pos - pos);
uint32 number = 0;
auto [ptr, ec] = std::from_chars(number_str.data(), number_str.data() + number_str.size(), number);
if (ec == std::errc())
{
ids.insert(number);
}
pos = end_pos;
}
return ids;
}
std::set<uint32> ChatHelper::ExtractAllQuestIds(const std::string& text)
{
return extractGeneric(text, "Hquest:");
}
std::set<uint32> ChatHelper::ExtractAllItemIds(const std::string& text)
{
return extractGeneric(text, "Hitem:");
}

89
src/Bot/Cmd/ChatHelper.h Normal file
View File

@@ -0,0 +1,89 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_CHATHELPER_H
#define _PLAYERBOT_CHATHELPER_H
#include <map>
#include "Common.h"
#include "ObjectGuid.h"
#include "PlayerbotAIAware.h"
#include "SharedDefines.h"
class GameObject;
class Quest;
class Player;
class PlayerbotAI;
class SpellInfo;
class WorldObject;
struct ItemTemplate;
typedef std::set<uint32> ItemIds;
typedef std::set<uint32> SpellIds;
struct ItemWithRandomProperty {
uint32 itemId{0};
int32 randomPropertyId{0};
};
class ChatHelper : public PlayerbotAIAware
{
public:
ChatHelper(PlayerbotAI* botAI);
static std::string const formatMoney(uint32 copper);
static uint32 parseMoney(std::string const text);
static ItemIds parseItems(std::string const text);
static ItemWithRandomProperty parseItemWithRandomProperty(std::string const text);
uint32 parseSpell(std::string const text);
static std::string parseValue(const std::string& type, const std::string& text);
static std::string const FormatQuest(Quest const* quest);
static std::string const FormatItem(ItemTemplate const* proto, uint32 count = 0, uint32 total = 0);
static std::string const FormatQItem(uint32 itemId);
static std::string const FormatSpell(SpellInfo const* spellInfo);
static std::string const FormatGameobject(GameObject* go);
static std::string const FormatWorldobject(WorldObject* wo);
static std::string const FormatWorldEntry(int32 entry);
static std::string const FormatQuestObjective(std::string const name, uint32 available, uint32 required);
static GuidVector parseGameobjects(std::string const text);
static ChatMsg parseChat(std::string const text);
static std::string const FormatChat(ChatMsg chat);
static std::string const FormatClass(Player* player, int8 spec);
static std::string const FormatClass(uint8 cls);
static std::string const FormatRace(uint8 race);
static std::string const FormatSkill(uint32 skill);
static std::string const FormatBoolean(bool flag);
static uint32 parseItemQuality(std::string const text);
static bool parseItemClass(std::string const text, uint32* itemClass, uint32* itemSubClass);
static uint32 parseSlot(std::string const text);
uint32 parseSkill(std::string const text);
static bool parseable(std::string const text);
void eraseAllSubStr(std::string& mainStr, std::string const toErase);
static std::set<uint32> ExtractAllQuestIds(const std::string& text);
static std::set<uint32> ExtractAllItemIds(const std::string& text);
private:
static std::map<std::string, uint32> consumableSubClasses;
static std::map<std::string, uint32> tradeSubClasses;
static std::map<std::string, uint32> itemQualities;
static std::map<std::string, uint32> projectileSubClasses;
static std::map<std::string, uint32> slots;
static std::map<std::string, uint32> skills;
static std::map<std::string, ChatMsg> chats;
static std::map<uint8, std::string> classes;
static std::map<uint8, std::string> races;
static std::map<uint8, std::map<uint8, std::string>> specs;
};
#endif

View File

@@ -0,0 +1,100 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "PlayerbotCommandServer.h"
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/smart_ptr.hpp>
#include <boost/thread/thread.hpp>
#include <cstdlib>
#include <iostream>
#include "IoContext.h"
#include "Playerbots.h"
using boost::asio::ip::tcp;
typedef boost::shared_ptr<tcp::socket> socket_ptr;
bool ReadLine(socket_ptr sock, std::string* buffer, std::string* line)
{
// Do the real reading from fd until buffer has '\n'.
std::string::iterator pos;
while ((pos = find(buffer->begin(), buffer->end(), '\n')) == buffer->end())
{
char buf[1025];
boost::system::error_code error;
size_t n = sock->read_some(boost::asio::buffer(buf), error);
if (n == -1 || error == boost::asio::error::eof)
return false;
else if (error)
throw boost::system::system_error(error); // Some other error.
buf[n] = 0;
*buffer += buf;
}
*line = std::string(buffer->begin(), pos);
*buffer = std::string(pos + 1, buffer->end());
return true;
}
void session(socket_ptr sock)
{
try
{
std::string buffer, request;
while (ReadLine(sock, &buffer, &request))
{
std::string const response = sRandomPlayerbotMgr->HandleRemoteCommand(request) + "\n";
boost::asio::write(*sock, boost::asio::buffer(response.c_str(), response.size()));
request = "";
}
}
catch (std::exception& e)
{
LOG_ERROR("playerbots", "{}", e.what());
}
}
void server(Acore::Asio::IoContext& io_service, short port)
{
tcp::acceptor a(io_service, tcp::endpoint(tcp::v4(), port));
for (;;)
{
socket_ptr sock(new tcp::socket(io_service));
a.accept(*sock);
boost::thread t(boost::bind(session, sock));
}
}
void Run()
{
if (!sPlayerbotAIConfig->commandServerPort)
{
return;
}
std::ostringstream s;
s << "Starting Playerbots Command Server on port " << sPlayerbotAIConfig->commandServerPort;
LOG_INFO("playerbots", "{}", s.str().c_str());
try
{
Acore::Asio::IoContext io_service;
server(io_service, sPlayerbotAIConfig->commandServerPort);
}
catch (std::exception& e)
{
LOG_ERROR("playerbots", "{}", e.what());
}
}
void PlayerbotCommandServer::Start()
{
std::thread serverThread(Run);
serverThread.detach();
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_PLAYERBOTCOMMANDSERVER_H
#define _PLAYERBOT_PLAYERBOTCOMMANDSERVER_H
class PlayerbotCommandServer
{
public:
PlayerbotCommandServer() {}
virtual ~PlayerbotCommandServer() {}
static PlayerbotCommandServer* instance()
{
static PlayerbotCommandServer instance;
return &instance;
}
void Start();
};
#define sPlayerbotCommandServer PlayerbotCommandServer::instance()
#endif