diff options
author | Treeston <treeston.mmoc@gmail.com> | 2020-09-20 02:50:38 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-09-20 02:50:38 +0200 |
commit | 1eca51b417678b9a48b28552925d5694105f82bb (patch) | |
tree | 9ae5150e9efbd4f2ad2364922cab46093ee25419 /src/server/game/Chat/Chat.cpp | |
parent | a724903b8b307516780474dd23977d2a9b502eb5 (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.cpp | 387 |
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; } |