summaryrefslogtreecommitdiff
path: root/src/server/game/Chat/Chat.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/game/Chat/Chat.cpp')
-rw-r--r--src/server/game/Chat/Chat.cpp555
1 files changed, 59 insertions, 496 deletions
diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp
index 014c78124d..80d6104dd4 100644
--- a/src/server/game/Chat/Chat.cpp
+++ b/src/server/game/Chat/Chat.cpp
@@ -15,10 +15,9 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include "Chat.h"
#include "AccountMgr.h"
#include "CellImpl.h"
-#include "Chat.h"
-#include "ChatLink.h"
#include "Common.h"
#include "DatabaseEnv.h"
#include "GridNotifiersImpl.h"
@@ -30,6 +29,7 @@
#include "Realm.h"
#include "ScriptMgr.h"
#include "SpellMgr.h"
+#include "Tokenize.h"
#include "UpdateMask.h"
#include "World.h"
#include "WorldPacket.h"
@@ -39,45 +39,9 @@
#include "LuaEngine.h"
#endif
-bool ChatHandler::load_command_table = true;
-
-std::vector<ChatCommand> const& ChatHandler::getCommandTable()
+Player* ChatHandler::GetPlayer() const
{
- static std::vector<ChatCommand> commandTableCache;
-
- if (LoadCommandTable())
- {
- SetLoadCommandTable(false);
-
- std::vector<ChatCommand> cmds = sScriptMgr->GetChatCommands();
- commandTableCache.swap(cmds);
-
- 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].GetUInt8(), fields[2].GetString(), name);
- } while (result->NextRow());
- }
- }
-
- return commandTableCache;
-}
-
-std::string ChatHandler::PGetParseString(uint32 entry, ...) const
-{
- const char* format = GetAcoreString(entry);
- char str[1024];
- va_list ap;
- va_start(ap, entry);
- vsnprintf(str, 1024, format, ap);
- va_end(ap);
- return std::string(str);
+ return m_session ? m_session->GetPlayer() : nullptr;
}
char const* ChatHandler::GetAcoreString(uint32 entry) const
@@ -85,10 +49,10 @@ char const* ChatHandler::GetAcoreString(uint32 entry) const
return m_session->GetAcoreString(entry);
}
-bool ChatHandler::isAvailable(ChatCommand const& cmd) const
+bool ChatHandler::IsAvailable(uint32 securityLevel) const
{
// check security level only for simple command (without child commands)
- return m_session->GetSecurity() >= AccountTypes(cmd.SecurityLevel);
+ return m_session->GetSecurity() >= AccountTypes(securityLevel);
}
bool ChatHandler::HasLowerSecurity(Player* target, ObjectGuid guid, bool strong)
@@ -141,83 +105,50 @@ bool ChatHandler::HasLowerSecurityAccount(WorldSession* target, uint32 target_ac
return false;
}
-bool ChatHandler::hasStringAbbr(const char* name, const char* part)
+void ChatHandler::SendSysMessage(std::string_view str, bool escapeCharacters)
{
- // non "" command
- if (*name)
+ std::string msg{ str };
+
+ // Replace every "|" with "||" in msg
+ if (escapeCharacters && msg.find('|') != std::string::npos)
{
- // "" part from non-"" command
- if (!*part)
- return false;
+ std::vector<std::string_view> tokens = Acore::Tokenize(msg, '|', true);
+ std::ostringstream stream;
- while (true)
- {
- if (!*part)
- return true;
- else if (!*name)
- return false;
- else if (tolower(*name) != tolower(*part))
- return false;
- ++name;
- ++part;
- }
- }
- // allow with any for ""
+ for (size_t i = 0; i < tokens.size() - 1; ++i)
+ stream << tokens[i] << "||";
- return true;
-}
+ stream << tokens[tokens.size() - 1];
-void ChatHandler::SendSysMessage(const char* str)
-{
- WorldPacket data;
-
- // need copy to prevent corruption by strtok call in LineFromMessage original string
- char* buf = strdup(str);
- char* pos = buf;
+ msg = stream.str();
+ }
- while (char* line = LineFromMessage(pos))
+ WorldPacket data;
+ for (std::string_view line : Acore::Tokenize(str, '\n', true))
{
BuildChatPacket(data, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, nullptr, nullptr, line);
m_session->SendPacket(&data);
}
-
- free(buf);
}
void ChatHandler::SendGlobalSysMessage(const char* str)
{
- // Chat output
WorldPacket data;
-
- // need copy to prevent corruption by strtok call in LineFromMessage original string
- char* buf = strdup(str);
- char* pos = buf;
-
- while (char* line = LineFromMessage(pos))
+ for (std::string_view line : Acore::Tokenize(str, '\n', true))
{
BuildChatPacket(data, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, nullptr, nullptr, line);
sWorld->SendGlobalMessage(&data);
}
-
- free(buf);
}
void ChatHandler::SendGlobalGMSysMessage(const char* str)
{
- // Chat output
WorldPacket data;
-
- // need copy to prevent corruption by strtok call in LineFromMessage original string
- char* buf = strdup(str);
- char* pos = buf;
-
- while (char* line = LineFromMessage(pos))
+ for (std::string_view line : Acore::Tokenize(str, '\n', true))
{
BuildChatPacket(data, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, nullptr, nullptr, line);
sWorld->SendGlobalGMMessage(&data);
}
-
- free(buf);
}
void ChatHandler::SendSysMessage(uint32 entry)
@@ -225,388 +156,42 @@ void ChatHandler::SendSysMessage(uint32 entry)
SendSysMessage(GetAcoreString(entry));
}
-void ChatHandler::PSendSysMessage(uint32 entry, ...)
-{
- const char* format = GetAcoreString(entry);
- va_list ap;
- char str [2048];
- va_start(ap, entry);
- vsnprintf(str, 2048, format, ap);
- va_end(ap);
- SendSysMessage(str);
-}
-
-void ChatHandler::PSendSysMessage(const char* format, ...)
-{
- va_list ap;
- char str [2048];
- va_start(ap, format);
- vsnprintf(str, 2048, format, ap);
- va_end(ap);
- SendSysMessage(str);
-}
-
-bool ChatHandler::ExecuteCommandInTable(std::vector<ChatCommand> const& table, const char* text, std::string const& fullcmd)
-{
- 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.c_str()))
- {
-#ifdef ELUNA
- if (!sEluna->OnCommand(GetSession() ? GetSession()->GetPlayer() : nullptr, oldtext))
- return true;
-#endif
- 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].Handler || !isAvailable(table[i]))
- continue;
-
- SetSentErrorMessage(false);
- // table[i].Name == "" is special case: send original command to handler
- if ((table[i].Handler)(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();
- uint32 zoneId = player->GetZoneId();
- std::string areaName = "Unknown";
- std::string zoneName = "Unknown";
- int locale = GetSessionDbcLocale();
-
- if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(areaId))
- {
- areaName = area->area_name[locale];
- }
-
- if (AreaTableEntry const* zone = sAreaTableStore.LookupEntry(zoneId))
- {
- zoneName = zone->area_name[locale];
- }
-
- LOG_GM(m_session->GetAccountId(), "Command: %s [Player: %s (%s) (Account: %u) X: %f Y: %f Z: %f Map: %u (%s) Area: %u (%s) Zone: %u (%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->GetMap()->GetMapName(),
- areaId, areaName.c_str(), zoneId, 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);
- }
-
- return true;
- }
-
- return false;
-}
-
-bool ChatHandler::SetDataForCommandInTable(std::vector<ChatCommand>& table, char const* text, uint32 security, std::string const& help, std::string const& fullcommand)
+bool ChatHandler::_ParseCommands(std::string_view text)
{
- 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, security, 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)
- {
- LOG_ERROR("sql.sql", "Table `command` have unexpected subcommand '%s' in command '%s', skip.", text, fullcommand.c_str());
- return false;
- }
-
- table[i].SecurityLevel = security;
- table[i].Help = help;
+ if (Acore::ChatCommands::TryExecuteCommand(*this, text))
return true;
- }
-
- // in case "" command let process by caller
- if (!cmd.empty())
- {
- if (&table == &getCommandTable())
- LOG_ERROR("sql.sql", "Table `command` have non-existing command '%s', skip.", cmd.c_str());
- else
- LOG_ERROR("sql.sql", "Table `command` have non-existing subcommand '%s' in command '%s', skip.", cmd.c_str(), fullcommand.c_str());
- }
-
- return false;
-}
-
-bool ChatHandler::ParseCommands(char const* text)
-{
- ASSERT(text);
- ASSERT(*text);
-
- std::string fullcmd = text;
+ // Pretend commands don't exist for regular players
if (m_session && AccountMgr::IsPlayerAccount(m_session->GetSecurity()) && !sWorld->getBoolConfig(CONFIG_ALLOW_PLAYER_COMMANDS))
return false;
- /// chat case (.command or !command format)
- if (m_session)
- {
- if (text[0] != '!' && text[0] != '.')
- return false;
- }
-
- /// ignore single . and ! in line
- if (strlen(text) < 2)
- return false;
- // original `text` can't be used. It content destroyed in command code processing.
-
- /// ignore messages staring from many dots.
- if ((text[0] == '.' && text[1] == '.') || (text[0] == '!' && text[1] == '!'))
- return false;
-
- /// skip first . or ! (in console allowed use command with . and ! and without its)
- if (text[0] == '!' || text[0] == '.')
- ++text;
-
- if (!ExecuteCommandInTable(getCommandTable(), text, fullcmd))
- {
-#ifdef ELUNA
- if (!sEluna->OnCommand(GetSession() ? GetSession()->GetPlayer() : nullptr, text))
- return true;
-#endif
- if (m_session && AccountMgr::IsPlayerAccount(m_session->GetSecurity()))
- return false;
-
- SendSysMessage(LANG_NO_CMD);
- }
+ // Send error message for GMs
+ PSendSysMessage(LANG_CMD_INVALID, STRING_VIEW_FMT_ARG(text));
+ SetSentErrorMessage(true);
return true;
}
-bool ChatHandler::isValidChatMessage(char const* message)
+bool ChatHandler::ParseCommands(std::string_view text)
{
- /*
- Valid examples:
- |cffa335ee|Hitem:812:0:0:0:0:0:0:0:70|h[Glowing Brightwood Staff]|h|r
- |cff808080|Hquest:2278:47|h[The Platinum Discs]|h|r
- |cffffd000|Htrade:4037:1:150:1:6AAAAAAAAAAAAAAAAAAAAAAOAADAAAAAAAAAAAAAAAAIAAAAAAAAA|h[Engineering]|h|r
- |cff4e96f7|Htalent:2232:-1|h[Taste for Blood]|h|r
- |cff71d5ff|Hspell:21563|h[Command]|h|r
- |cffffd000|Henchant:3919|h[Engineering: Rough Dynamite]|h|r
- |cffffff00|Hachievement:546:0000000000000001:0:0:0:-1:0:0:0:0|h[Safe Deposit]|h|r
- |cff66bbff|Hglyph:21:762|h[Glyph of Bladestorm]|h|r
-
- | will be escaped to ||
- */
-
- if (strlen(message) > 255)
- return false;
-
- // more simple checks
- if (sWorld->getIntConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY) < 3)
- {
- const char validSequence[6] = "cHhhr";
- const char* validSequenceIterator = validSequence;
- const std::string validCommands = "cHhr|";
+ ASSERT(!text.empty());
- while (*message)
- {
- // find next pipe command
- message = strchr(message, '|');
-
- if (!message)
- return true;
-
- ++message;
- char commandChar = *message;
- if (validCommands.find(commandChar) == std::string::npos)
- return false;
-
- ++message;
- // validate sequence
- if (sWorld->getIntConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY) == 2)
- {
- if (commandChar == *validSequenceIterator)
- {
- if (validSequenceIterator == validSequence + 4)
- validSequenceIterator = validSequence;
- else
- ++validSequenceIterator;
- }
- else
- return false;
- }
- }
- return true;
- }
-
- return LinkExtractor(message).IsValidMessage();
-}
-
-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 for 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())
+ // chat case (.command or !command format)
+ if ((text[0] != '!') && (text[0] != '.'))
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, const char* cmd)
-{
- if (*cmd)
- {
- for (uint32 i = 0; i < table.size(); ++i)
- {
- // must be available (ignore handler existence for show command with possible available subcommands)
- if (!isAvailable(table[i]))
- continue;
-
- if (!hasStringAbbr(table[i].Name, cmd))
- continue;
-
- // have subcommand
- char const* subcmd = (*cmd) ? strtok(nullptr, " ") : "";
-
- if (!table[i].ChildCommands.empty() && subcmd && *subcmd)
- {
- if (ShowHelpForCommand(table[i].ChildCommands, subcmd))
- 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 ? subcmd : ""))
- return true;
-
- return !table[i].Help.empty();
- }
- }
- else
- {
- for (uint32 i = 0; i < table.size(); ++i)
- {
- // must be available (ignore handler existence for 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());
+ // ignore single . and ! in line
+ if (text.length() < 2)
+ return false;
- if (!table[i].ChildCommands.empty())
- if (ShowHelpForSubCommands(table[i].ChildCommands, "", ""))
- return true;
+ // ignore messages staring from many dots.
+ if (text[1] == text[0])
+ return false;
- return !table[i].Help.empty();
- }
- }
+ // ignore messages with separator after .
+ if (text[1] == Acore::Impl::ChatCommands::COMMAND_DELIMITER)
+ return false;
- 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,
@@ -1009,21 +594,6 @@ uint32 ChatHandler::extractSpellIdFromLink(char* text)
return 0;
}
-GameTele const* ChatHandler::extractGameTeleFromLink(char* text)
-{
- // id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r
- char* cId = extractKeyFromLink(text, "Htele");
- if (!cId)
- return nullptr;
-
- // id case (explicit or from shift link)
- if (cId[0] >= '0' || cId[0] <= '9')
- if (uint32 id = atoi(cId))
- return sObjectMgr->GetGameTele(id);
-
- return sObjectMgr->GetGameTele(cId);
-}
-
enum GuidLinkType
{
SPELL_LINK_PLAYER = 0, // must be first for selection in not link case
@@ -1140,10 +710,15 @@ bool ChatHandler::extractPlayerTarget(char* args, Player** player, ObjectGuid* p
}
else
{
+ // populate strtok buffer to prevent crashes
+ static char dummy[1] = "";
+ strtok(dummy, "");
+
Player* pl = getSelectedPlayer();
// if allowed player pointer
if (player)
*player = pl;
+
// if allowed player guid (if no then only online players allowed)
if (player_guid)
*player_guid = pl ? pl->GetGUID() : ObjectGuid::Empty;
@@ -1163,24 +738,6 @@ bool ChatHandler::extractPlayerTarget(char* args, Player** player, ObjectGuid* p
return true;
}
-void ChatHandler::extractOptFirstArg(char* args, char** arg1, char** arg2)
-{
- char* p1 = strtok(args, " ");
- char* p2 = strtok(nullptr, " ");
-
- if (!p2)
- {
- p2 = p1;
- p1 = nullptr;
- }
-
- if (arg1)
- *arg1 = p1;
-
- if (arg2)
- *arg2 = p2;
-}
-
char* ChatHandler::extractQuotedArg(char* args)
{
if (!*args)
@@ -1247,16 +804,22 @@ char const* CliHandler::GetAcoreString(uint32 entry) const
return sObjectMgr->GetAcoreStringForDBCLocale(entry);
}
-bool CliHandler::isAvailable(ChatCommand const& cmd) const
+void CliHandler::SendSysMessage(std::string_view str, bool /*escapeCharacters*/)
{
- // skip non-console commands in console case
- return cmd.AllowConsole;
+ m_print(m_callbackArg, str);
+ m_print(m_callbackArg, "\r\n");
}
-void CliHandler::SendSysMessage(const char* str)
+bool CliHandler::ParseCommands(std::string_view str)
{
- m_print(m_callbackArg, str);
- m_print(m_callbackArg, "\r\n");
+ if (str.empty())
+ return false;
+
+ // Console allows using commands both with and without leading indicator
+ if (str[0] == '.' || str[0] == '!')
+ str = str.substr(1);
+
+ return _ParseCommands(str);
}
std::string CliHandler::GetNameLink() const