aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Chat/Chat.cpp
diff options
context:
space:
mode:
authorTreeston <treeston.mmoc@gmail.com>2020-09-20 02:50:38 +0200
committerGitHub <noreply@github.com>2020-09-20 02:50:38 +0200
commit1eca51b417678b9a48b28552925d5694105f82bb (patch)
tree9ae5150e9efbd4f2ad2364922cab46093ee25419 /src/server/game/Chat/Chat.cpp
parenta724903b8b307516780474dd23977d2a9b502eb5 (diff)
[3.3.5] ChatCommands, the other half: chat command resolution refactor (PR #25463)
Diffstat (limited to 'src/server/game/Chat/Chat.cpp')
-rw-r--r--src/server/game/Chat/Chat.cpp387
1 files changed, 28 insertions, 359 deletions
diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp
index e1af000ab00..3839acf18bc 100644
--- a/src/server/game/Chat/Chat.cpp
+++ b/src/server/game/Chat/Chat.cpp
@@ -34,58 +34,13 @@
#include "WorldSession.h"
#include <boost/algorithm/string/replace.hpp>
-Player* ChatHandler::GetPlayer() { return m_session ? m_session->GetPlayer() : nullptr; }
-
-// Lazy loading of the command table cache from commands and the
-// ScriptMgr should be thread safe since the player commands,
-// cli commands and ScriptMgr updates are all dispatched one after
-// one inside the world update loop.
-static Optional<std::vector<ChatCommand>> commandTableCache;
-
-std::vector<ChatCommand> const& ChatHandler::getCommandTable()
-{
- if (!commandTableCache)
- InitializeCommandTable();
-
- return *commandTableCache;
-}
-
-void ChatHandler::InitializeCommandTable()
-{
- // We need to initialize this at top since SetDataForCommandInTable
- // calls getCommandTable() recursively.
- commandTableCache = sScriptMgr->GetChatCommands();
-
- WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_COMMANDS);
- PreparedQueryResult result = WorldDatabase.Query(stmt);
- if (result)
- {
- do
- {
- Field* fields = result->Fetch();
- std::string name = fields[0].GetString();
-
- SetDataForCommandInTable(*commandTableCache, name.c_str(), fields[1].GetUInt16(), fields[2].GetString(), name);
- }
- while (result->NextRow());
- }
-}
-
-void ChatHandler::invalidateCommandTable()
-{
- commandTableCache.reset();
-}
+Player* ChatHandler::GetPlayer() const { return m_session ? m_session->GetPlayer() : nullptr; }
char const* ChatHandler::GetTrinityString(uint32 entry) const
{
return m_session->GetTrinityString(entry);
}
-bool ChatHandler::isAvailable(ChatCommand const& cmd) const
-{
- return HasPermission(cmd.Permission);
-}
-
bool ChatHandler::HasPermission(uint32 permission) const
{
return m_session->HasPermission(permission);
@@ -146,31 +101,6 @@ bool ChatHandler::HasLowerSecurityAccount(WorldSession* target, uint32 target_ac
return false;
}
-bool ChatHandler::hasStringAbbr(char const* name, char const* part)
-{
- // non "" command
- if (*name)
- {
- // "" part from non-"" command
- if (!*part)
- return false;
-
- while (true)
- {
- if (!*part || *part == ' ')
- return true;
- else if (!*name)
- return false;
- else if (tolower(*name) != tolower(*part))
- return false;
- ++name; ++part;
- }
- }
- // allow with any for ""
-
- return true;
-}
-
void ChatHandler::SendSysMessage(std::string_view str, bool escapeCharacters)
{
std::string msg{ str };
@@ -220,171 +150,9 @@ void ChatHandler::SendSysMessage(uint32 entry)
SendSysMessage(GetTrinityString(entry));
}
-bool ChatHandler::ExecuteCommandInTable(std::vector<ChatCommand> const& table, char const* text, std::string const& fullcmd)
+bool ChatHandler::_ParseCommands(std::string_view text)
{
- char const* oldtext = text;
- std::string cmd = "";
-
- while (*text != ' ' && *text != '\0')
- {
- cmd += *text;
- ++text;
- }
-
- while (*text == ' ') ++text;
-
- for (uint32 i = 0; i < table.size(); ++i)
- {
- if (!hasStringAbbr(table[i].Name, cmd.c_str()))
- continue;
-
- bool match = false;
- if (strlen(table[i].Name) > cmd.length())
- {
- for (uint32 j = 0; j < table.size(); ++j)
- {
- if (!hasStringAbbr(table[j].Name, cmd.c_str()))
- continue;
-
- if (strcmp(table[j].Name, cmd.c_str()) == 0)
- {
- match = true;
- break;
- }
- }
- }
- if (match)
- continue;
-
- // select subcommand from child commands list
- if (!table[i].ChildCommands.empty())
- {
- if (!ExecuteCommandInTable(table[i].ChildCommands, text, fullcmd))
- {
- if (m_session && !m_session->HasPermission(rbac::RBAC_PERM_COMMANDS_NOTIFY_COMMAND_NOT_FOUND_ERROR))
- return false;
-
- if (text[0] != '\0')
- SendSysMessage(LANG_NO_SUBCMD);
- else
- SendSysMessage(LANG_CMD_SYNTAX);
-
- ShowHelpForCommand(table[i].ChildCommands, text);
- }
-
- return true;
- }
-
- // must be available and have handler
- if (!table[i].HasHandler() || !isAvailable(table[i]))
- continue;
-
- SetSentErrorMessage(false);
- // table[i].Name == "" is special case: send original command to handler
- if (table[i](this, table[i].Name[0] != '\0' ? text : oldtext))
- {
- if (!m_session) // ignore console
- return true;
-
- Player* player = m_session->GetPlayer();
- if (!AccountMgr::IsPlayerAccount(m_session->GetSecurity()))
- {
- ObjectGuid guid = player->GetTarget();
- uint32 areaId = player->GetAreaId();
- std::string areaName = "Unknown";
- std::string zoneName = "Unknown";
- if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(areaId))
- {
- int locale = GetSessionDbcLocale();
- areaName = area->AreaName[locale];
- if (AreaTableEntry const* zone = sAreaTableStore.LookupEntry(area->ParentAreaID))
- zoneName = zone->AreaName[locale];
- }
-
- sLog->outCommand(m_session->GetAccountId(), "Command: %s [Player: %s (%s) (Account: %u) X: %f Y: %f Z: %f Map: %u (%s) Area: %u (%s) Zone: %s Selected: %s (%s)]",
- fullcmd.c_str(), player->GetName().c_str(), player->GetGUID().ToString().c_str(),
- m_session->GetAccountId(), player->GetPositionX(), player->GetPositionY(),
- player->GetPositionZ(), player->GetMapId(),
- player->FindMap() ? player->FindMap()->GetMapName() : "Unknown",
- areaId, areaName.c_str(), zoneName.c_str(),
- (player->GetSelectedUnit()) ? player->GetSelectedUnit()->GetName().c_str() : "",
- guid.ToString().c_str());
- }
- }
- // some commands have custom error messages. Don't send the default one in these cases.
- else if (!HasSentErrorMessage())
- {
- if (!table[i].Help.empty())
- SendSysMessage(table[i].Help.c_str());
- else
- SendSysMessage(LANG_CMD_SYNTAX);
- SetSentErrorMessage(true);
- }
-
- return true;
- }
-
- return false;
-}
-
-bool ChatHandler::SetDataForCommandInTable(std::vector<ChatCommand>& table, char const* text, uint32 permission, std::string const& help, std::string const& fullcommand)
-{
- std::string cmd = "";
-
- while (*text != ' ' && *text != '\0')
- {
- cmd += *text;
- ++text;
- }
-
- while (*text == ' ') ++text;
-
- for (uint32 i = 0; i < table.size(); i++)
- {
- // for data fill use full explicit command names
- if (table[i].Name != cmd)
- continue;
-
- // select subcommand from child commands list (including "")
- if (!table[i].ChildCommands.empty())
- {
- if (SetDataForCommandInTable(table[i].ChildCommands, text, permission, help, fullcommand))
- return true;
- else if (*text)
- return false;
-
- // fail with "" subcommands, then use normal level up command instead
- }
- // expected subcommand by full name DB content
- else if (*text)
- {
- TC_LOG_ERROR("sql.sql", "Table `command` contains an unexpected subcommand '%s' in command '%s', skipped.", text, fullcommand.c_str());
- return false;
- }
-
- if (table[i].Permission != permission)
- TC_LOG_INFO("misc", "Table `command` overwrite for command '%s' default permission (%u) by %u", fullcommand.c_str(), table[i].Permission, permission);
-
- table[i].Permission = permission;
- table[i].Help = help;
- return true;
- }
-
- // in case "" command let process by caller
- if (!cmd.empty())
- {
- if (&table == &getCommandTable())
- TC_LOG_ERROR("sql.sql", "Table `command` contains a non-existing command '%s', skipped.", cmd.c_str());
- else
- TC_LOG_ERROR("sql.sql", "Table `command` contains a non-existing subcommand '%s' in command '%s', skipped.", cmd.c_str(), fullcommand.c_str());
- }
-
- return false;
-}
-
-bool ChatHandler::_ParseCommands(char const* text)
-{
- if (ExecuteCommandInTable(getCommandTable(), text, text))
+ if (Trinity::ChatCommands::TryExecuteCommand(*this, text))
return true;
// Pretend commands don't exist for regular players
@@ -392,126 +160,32 @@ bool ChatHandler::_ParseCommands(char const* text)
return false;
// Send error message for GMs
- SendSysMessage(LANG_NO_CMD);
+ PSendSysMessage(LANG_CMD_INVALID, STRING_VIEW_FMT_ARG(text));
SetSentErrorMessage(true);
return true;
}
-bool ChatHandler::ParseCommands(char const* text)
+bool ChatHandler::ParseCommands(std::string_view text)
{
- ASSERT(text);
- ASSERT(*text);
+ ASSERT(!text.empty());
- /// chat case (.command or !command format)
- if (text[0] != '!' && text[0] != '.')
+ // chat case (.command or !command format)
+ if ((text[0] != '!') && (text[0] != '.'))
return false;
- /// ignore single . and ! in line
- if (!text[1])
+ // ignore single . and ! in line
+ if (text.length() < 2)
return false;
- /// ignore messages staring from many dots.
- if (text[1] == '!' || text[1] == '.')
+ // ignore messages staring from many dots.
+ if (text[1] == text[0])
return false;
- return _ParseCommands(text+1);
-}
-
-bool ChatHandler::ShowHelpForSubCommands(std::vector<ChatCommand> const& table, char const* cmd, char const* subcmd)
-{
- std::string list;
- for (uint32 i = 0; i < table.size(); ++i)
- {
- // must be available (ignore handler existence to show command with possible available subcommands)
- if (!isAvailable(table[i]))
- continue;
-
- // for empty subcmd show all available
- if (*subcmd && !hasStringAbbr(table[i].Name, subcmd))
- continue;
-
- if (m_session)
- list += "\n ";
- else
- list += "\n\r ";
-
- list += table[i].Name;
-
- if (!table[i].ChildCommands.empty())
- list += " ...";
- }
-
- if (list.empty())
+ // ignore messages with separator after .
+ if (text[1] == Trinity::Impl::ChatCommands::COMMAND_DELIMITER)
return false;
- if (&table == &getCommandTable())
- {
- SendSysMessage(LANG_AVIABLE_CMD);
- PSendSysMessage("%s", list.c_str());
- }
- else
- PSendSysMessage(LANG_SUBCMDS_LIST, cmd, list.c_str());
-
- return true;
-}
-
-bool ChatHandler::ShowHelpForCommand(std::vector<ChatCommand> const& table, char const* cmd)
-{
- if (*cmd)
- {
- std::string subcmd;
- if (size_t n = std::string_view(cmd).find(' '); n != std::string_view::npos)
- subcmd.assign(cmd+n+1);
-
- for (uint32 i = 0; i < table.size(); ++i)
- {
- // must be available (ignore handler existence to show command with possible available subcommands)
- if (!isAvailable(table[i]))
- continue;
-
- if (!hasStringAbbr(table[i].Name, cmd))
- continue;
-
- // have subcommand
- if (!table[i].ChildCommands.empty() && !subcmd.empty())
- {
- if (ShowHelpForCommand(table[i].ChildCommands, subcmd.c_str()))
- return true;
- }
-
- if (!table[i].Help.empty())
- SendSysMessage(table[i].Help.c_str());
-
- if (!table[i].ChildCommands.empty())
- if (ShowHelpForSubCommands(table[i].ChildCommands, table[i].Name, subcmd.c_str()))
- return true;
-
- return !table[i].Help.empty();
- }
- }
- else
- {
- for (uint32 i = 0; i < table.size(); ++i)
- {
- // must be available (ignore handler existence to show command with possible available subcommands)
- if (!isAvailable(table[i]))
- continue;
-
- if (strlen(table[i].Name))
- continue;
-
- if (!table[i].Help.empty())
- SendSysMessage(table[i].Help.c_str());
-
- if (!table[i].ChildCommands.empty())
- if (ShowHelpForSubCommands(table[i].ChildCommands, "", ""))
- return true;
-
- return !table[i].Help.empty();
- }
- }
-
- return ShowHelpForSubCommands(table, "", cmd);
+ return _ParseCommands(text.substr(1));
}
size_t ChatHandler::BuildChatPacket(WorldPacket& data, ChatMsg chatType, Language language, ObjectGuid senderGUID, ObjectGuid receiverGUID, std::string_view message, uint8 chatTag,
@@ -1099,25 +773,19 @@ char const* CliHandler::GetTrinityString(uint32 entry) const
return sObjectMgr->GetTrinityStringForDBCLocale(entry);
}
-bool CliHandler::isAvailable(ChatCommand const& cmd) const
-{
- // skip non-console commands in console case
- return cmd.AllowConsole;
-}
-
void CliHandler::SendSysMessage(std::string_view str, bool /*escapeCharacters*/)
{
m_print(m_callbackArg, str);
m_print(m_callbackArg, "\r\n");
}
-bool CliHandler::ParseCommands(char const* str)
+bool CliHandler::ParseCommands(std::string_view str)
{
- if (!str[0])
+ if (str.empty())
return false;
// Console allows using commands both with and without leading indicator
if (str[0] == '.' || str[0] == '!')
- ++str;
+ str = str.substr(1);
return _ParseCommands(str);
}
@@ -1185,16 +853,14 @@ int CliHandler::GetSessionDbLocaleIndex() const
return sObjectMgr->GetDBCLocaleIndex();
}
-bool AddonChannelCommandHandler::ParseCommands(char const* str)
+bool AddonChannelCommandHandler::ParseCommands(std::string_view str)
{
- if (memcmp(str, "TrinityCore\t", 12))
+ if (str.length() < 17)
return false;
- char opcode = str[12];
- if (!opcode) // str[12] is opcode
+ if (!StringStartsWith(str, "TrinityCore\t"))
return false;
- if (!str[13] || !str[14] || !str[15] || !str[16]) // str[13] through str[16] is 4-character command counter
- return false;
- echo = str+13;
+ char opcode = str[12];
+ echo = &str[13];
switch (opcode)
{
@@ -1203,10 +869,12 @@ bool AddonChannelCommandHandler::ParseCommands(char const* str)
return true;
case 'h': // h Issue human-readable command
case 'i': // i Issue command
+ {
if (!str[17])
return false;
humanReadable = (opcode == 'h');
- if (_ParseCommands(str + 17)) // actual command starts at str[17]
+ std::string_view cmd = str.substr(17);
+ if (_ParseCommands(cmd)) // actual command starts at str[17]
{
if (!hadAck)
SendAck();
@@ -1217,10 +885,11 @@ bool AddonChannelCommandHandler::ParseCommands(char const* str)
}
else
{
- SendSysMessage(LANG_NO_CMD);
+ PSendSysMessage(LANG_CMD_INVALID, STRING_VIEW_FMT_ARG(cmd));
SendFailed();
}
return true;
+ }
default:
return false;
}