summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.vscode/settings.json6
-rw-r--r--data/sql/updates/pending_db_world/rev_1622929985554698000.sql37
-rw-r--r--src/common/Define.h3
-rw-r--r--src/common/Utilities/Util.cpp64
-rw-r--r--src/common/Utilities/Util.h84
-rw-r--r--src/server/game/AuctionHouse/AuctionHouseMgr.cpp8
-rw-r--r--src/server/game/Battlegrounds/ArenaTeam.h1
-rw-r--r--src/server/game/Battlegrounds/BattlegroundMgr.h2
-rw-r--r--src/server/game/Battlegrounds/enuminfo_ArenaTeam.cpp56
-rw-r--r--src/server/game/Chat/Chat.cpp555
-rw-r--r--src/server/game/Chat/Chat.h82
-rw-r--r--src/server/game/Chat/ChatCommands/ChatCommand.cpp534
-rw-r--r--src/server/game/Chat/ChatCommands/ChatCommand.h280
-rw-r--r--src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp118
-rw-r--r--src/server/game/Chat/ChatCommands/ChatCommandArgs.h324
-rw-r--r--src/server/game/Chat/ChatCommands/ChatCommandHelpers.cpp31
-rw-r--r--src/server/game/Chat/ChatCommands/ChatCommandHelpers.h132
-rw-r--r--src/server/game/Chat/ChatCommands/ChatCommandTags.cpp145
-rw-r--r--src/server/game/Chat/ChatCommands/ChatCommandTags.h313
-rw-r--r--src/server/game/Chat/ChatLink.cpp714
-rw-r--r--src/server/game/Chat/ChatLink.h176
-rw-r--r--src/server/game/Chat/HyperlinkTags.cpp261
-rw-r--r--src/server/game/Chat/Hyperlinks.cpp424
-rw-r--r--src/server/game/Chat/Hyperlinks.h255
-rw-r--r--src/server/game/DataStores/DBCStores.cpp4
-rw-r--r--src/server/game/DataStores/DBCStores.h2
-rw-r--r--src/server/game/Entities/Item/Item.cpp4
-rw-r--r--src/server/game/Entities/Player/PlayerStorage.cpp14
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp6
-rw-r--r--src/server/game/Globals/ObjectMgr.h11
-rw-r--r--src/server/game/Handlers/ChannelHandler.cpp6
-rw-r--r--src/server/game/Handlers/ChatHandler.cpp35
-rw-r--r--src/server/game/Handlers/TicketHandler.cpp26
-rw-r--r--src/server/game/Miscellaneous/Language.h35
-rw-r--r--src/server/game/Scripting/ScriptMgr.cpp16
-rw-r--r--src/server/game/Scripting/ScriptMgr.h10
-rw-r--r--src/server/game/Server/Packets/PacketUtilities.cpp16
-rw-r--r--src/server/game/Server/Packets/PacketUtilities.h2
-rw-r--r--src/server/game/Server/WorldSession.cpp49
-rw-r--r--src/server/game/Server/WorldSession.h7
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.cpp4
-rw-r--r--src/server/game/Spells/Spell.cpp8
-rw-r--r--src/server/game/Spells/SpellInfo.cpp33
-rw-r--r--src/server/game/Spells/SpellInfo.h22
-rw-r--r--src/server/game/World/IWorld.h26
-rw-r--r--src/server/game/World/World.cpp15
-rw-r--r--src/server/scripts/Commands/cs_account.cpp20
-rw-r--r--src/server/scripts/Commands/cs_achievement.cpp12
-rw-r--r--src/server/scripts/Commands/cs_arena.cpp209
-rw-r--r--src/server/scripts/Commands/cs_ban.cpp34
-rw-r--r--src/server/scripts/Commands/cs_bf.cpp12
-rw-r--r--src/server/scripts/Commands/cs_cast.cpp12
-rw-r--r--src/server/scripts/Commands/cs_character.cpp142
-rw-r--r--src/server/scripts/Commands/cs_cheat.cpp12
-rw-r--r--src/server/scripts/Commands/cs_debug.cpp16
-rw-r--r--src/server/scripts/Commands/cs_deserter.cpp16
-rw-r--r--src/server/scripts/Commands/cs_disable.cpp16
-rw-r--r--src/server/scripts/Commands/cs_event.cpp12
-rw-r--r--src/server/scripts/Commands/cs_gm.cpp14
-rw-r--r--src/server/scripts/Commands/cs_go.cpp12
-rw-r--r--src/server/scripts/Commands/cs_gobject.cpp18
-rw-r--r--src/server/scripts/Commands/cs_guild.cpp31
-rw-r--r--src/server/scripts/Commands/cs_honor.cpp14
-rw-r--r--src/server/scripts/Commands/cs_instance.cpp12
-rw-r--r--src/server/scripts/Commands/cs_learn.cpp16
-rw-r--r--src/server/scripts/Commands/cs_lfg.cpp12
-rw-r--r--src/server/scripts/Commands/cs_list.cpp12
-rw-r--r--src/server/scripts/Commands/cs_lookup.cpp16
-rw-r--r--src/server/scripts/Commands/cs_message.cpp10
-rw-r--r--src/server/scripts/Commands/cs_misc.cpp82
-rw-r--r--src/server/scripts/Commands/cs_mmaps.cpp12
-rw-r--r--src/server/scripts/Commands/cs_modify.cpp18
-rw-r--r--src/server/scripts/Commands/cs_npc.cpp24
-rw-r--r--src/server/scripts/Commands/cs_player.cpp12
-rw-r--r--src/server/scripts/Commands/cs_quest.cpp12
-rw-r--r--src/server/scripts/Commands/cs_reload.cpp26
-rw-r--r--src/server/scripts/Commands/cs_reset.cpp12
-rw-r--r--src/server/scripts/Commands/cs_server.cpp38
-rw-r--r--src/server/scripts/Commands/cs_spectator.cpp26
-rw-r--r--src/server/scripts/Commands/cs_tele.cpp157
-rw-r--r--src/server/scripts/Commands/cs_ticket.cpp14
-rw-r--r--src/server/scripts/Commands/cs_titles.cpp14
-rw-r--r--src/server/scripts/Commands/cs_wp.cpp12
-rw-r--r--src/server/shared/DataStores/DBCStructure.h435
-rw-r--r--src/server/shared/SharedDefines.h10
-rw-r--r--src/server/worldserver/ACSoap/ACSoap.h4
-rw-r--r--src/server/worldserver/CommandLine/CliRunnable.cpp180
-rw-r--r--src/server/worldserver/RemoteAccess/RASession.cpp6
-rw-r--r--src/server/worldserver/RemoteAccess/RASession.h4
-rw-r--r--src/server/worldserver/worldserver.conf.dist30
90 files changed, 4268 insertions, 2496 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 5f14844b00..e48b4f25d1 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,5 +1,8 @@
{
"files.associations": {
+ "*.dist": "properties",
+ "*.crash": "properties",
+ "*.wtf": "properties",
"array": "cpp",
"atomic": "cpp",
"bit": "cpp",
@@ -62,7 +65,8 @@
"thread": "cpp",
"cfenv": "cpp",
"typeinfo": "cpp",
- "codecvt": "cpp"
+ "codecvt": "cpp",
+ "xstring": "cpp"
},
"deno.enable": true,
"deno.path": "deps/deno/bin/deno",
diff --git a/data/sql/updates/pending_db_world/rev_1622929985554698000.sql b/data/sql/updates/pending_db_world/rev_1622929985554698000.sql
new file mode 100644
index 0000000000..12a4aa93d2
--- /dev/null
+++ b/data/sql/updates/pending_db_world/rev_1622929985554698000.sql
@@ -0,0 +1,37 @@
+INSERT INTO `version_db_world` (`sql_rev`) VALUES ('1622929985554698000');
+
+DELETE FROM `acore_string` WHERE `entry` BETWEEN 1500 AND 1515;
+DELETE FROM `acore_string` WHERE `entry` IN (6, 7, 8, 191, 192, 193, 194, 195, 196);
+INSERT INTO `acore_string` (`entry`,`content_default`) VALUES
+( 6, 'Command \'%.*s\' does not exist'),
+( 7, 'Subcommand \'%.*s%c%.*s\' is ambiguous:'),
+( 8, 'Possible subcommands:'),
+(191, '|- %.*s'),
+(192, '|- %.*s ...'),
+(193, 'Subcommand \'%.*s%c%.*s\' does not exist.'),
+(194, 'Command \'%.*s\' is ambiguous:'),
+(195, '### USAGE: .%.*s ...'),
+(196, 'There is no detailed usage information associated with \'%.*s\'.
+This should never occur for stock AzerothCore commands - if it does, report this as a bug.'),
+(1500, 'Either:'),
+(1501, 'Or: '),
+(1502, 'Value \'%.*s\' is not valid for type %s.'),
+(1503, 'Invalid UTF-8 sequences found in string.'),
+(1504, 'Provided link has invalid link data.'),
+(1505, 'Account \'%.*s\' does not exist.'),
+(1506, 'Account ID %u does not exist.'),
+(1507, '%s does not exist.'),
+(1508, 'Character \'%.*s\' does not exist.'),
+(1509, '\'%.*s\' is not a valid character name.'),
+(1510, 'Achievement ID %u does not exist.'),
+(1511, 'Teleport location %u does not exist.'),
+(1512, 'Teleport location \'%.*s\' does not exist.'),
+(1513, 'Item ID %u does not exist.'),
+(1514, 'Spell ID %u does not exist.'),
+(1515, 'Expected \'%.*s\', got \'%.*s\' instead.');
+
+-- Correct
+UPDATE `command` SET `security`='3' WHERE `name` = 'debug anim';
+UPDATE `command` SET `security`='4' WHERE `name` = 'modify spell';
+UPDATE `command` SET `security`='3' WHERE `name` = 'npc set allowmove';
+UPDATE `command` SET `security`='3' WHERE `name` LIKE '%unban%';
diff --git a/src/common/Define.h b/src/common/Define.h
index d065370836..ab007ee89c 100644
--- a/src/common/Define.h
+++ b/src/common/Define.h
@@ -103,6 +103,9 @@
#define SZFMTD "%" PRIuPTR
+#define STRING_VIEW_FMT "%.*s"
+#define STRING_VIEW_FMT_ARG(str) static_cast<int>((str).length()), (str).data()
+
typedef std::int64_t int64;
typedef std::int32_t int32;
typedef std::int16_t int16;
diff --git a/src/common/Utilities/Util.cpp b/src/common/Utilities/Util.cpp
index ad0024085e..a942fd9a95 100644
--- a/src/common/Utilities/Util.cpp
+++ b/src/common/Utilities/Util.cpp
@@ -27,6 +27,7 @@
#include <iomanip>
#include <sstream>
#include <string>
+#include <boost/core/demangle.hpp>
#include <utf8.h>
Tokenizer::Tokenizer(const std::string& src, const char sep, uint32 vectorReserve)
@@ -409,12 +410,12 @@ bool Utf8toWStr(char const* utf8str, size_t csize, wchar_t* wstr, size_t& wsize)
return true;
}
-bool Utf8toWStr(const std::string& utf8str, std::wstring& wstr)
+bool Utf8toWStr(std::string_view utf8str, std::wstring& wstr)
{
wstr.clear();
try
{
- utf8::utf8to16(utf8str.c_str(), utf8str.c_str() + utf8str.size(), std::back_inserter(wstr));
+ utf8::utf8to16(utf8str.begin(), utf8str.end(), std::back_inserter(wstr));
}
catch (std::exception const&)
{
@@ -425,18 +426,19 @@ bool Utf8toWStr(const std::string& utf8str, std::wstring& wstr)
return true;
}
-bool WStrToUtf8(wchar_t* wstr, size_t size, std::string& utf8str)
+bool WStrToUtf8(wchar_t const* wstr, size_t size, std::string& utf8str)
{
try
{
std::string utf8str2;
- utf8str2.resize(size * 4); // allocate for most long case
+ utf8str2.resize(size * 4); // allocate for most long case
if (size)
{
char* oend = utf8::utf16to8(wstr, wstr + size, &utf8str2[0]);
- utf8str2.resize(oend - (&utf8str2[0])); // remove unused tail
+ utf8str2.resize(oend - (&utf8str2[0])); // remove unused tail
}
+
utf8str = utf8str2;
}
catch (std::exception const&)
@@ -448,18 +450,19 @@ bool WStrToUtf8(wchar_t* wstr, size_t size, std::string& utf8str)
return true;
}
-bool WStrToUtf8(std::wstring const& wstr, std::string& utf8str)
+bool WStrToUtf8(std::wstring_view wstr, std::string& utf8str)
{
try
{
std::string utf8str2;
- utf8str2.resize(wstr.size() * 4); // allocate for most long case
+ utf8str2.resize(wstr.size() * 4); // allocate for most long case
- if (wstr.size())
+ if (!wstr.empty())
{
- char* oend = utf8::utf16to8(wstr.c_str(), wstr.c_str() + wstr.size(), &utf8str2[0]);
- utf8str2.resize(oend - (&utf8str2[0])); // remove unused tail
+ char* oend = utf8::utf16to8(wstr.begin(), wstr.end(), &utf8str2[0]);
+ utf8str2.resize(oend - (&utf8str2[0])); // remove unused tail
}
+
utf8str = utf8str2;
}
catch (std::exception const&)
@@ -471,17 +474,10 @@ bool WStrToUtf8(std::wstring const& wstr, std::string& utf8str)
return true;
}
-typedef wchar_t const* const* wstrlist;
-
-void wstrToUpper(std::wstring& str)
-{
- std::transform(str.begin(), str.end(), str.begin(), wcharToUpper);
-}
-
-void wstrToLower(std::wstring& str)
-{
- std::transform(str.begin(), str.end(), str.begin(), wcharToLower);
-}
+void wstrToUpper(std::wstring& str) { std::transform(std::begin(str), std::end(str), std::begin(str), wcharToUpper); }
+void wstrToLower(std::wstring& str) { std::transform(std::begin(str), std::end(str), std::begin(str), wcharToLower); }
+void strToUpper(std::string& str) { std::transform(std::begin(str), std::end(str), std::begin(str), charToUpper); }
+void strToLower(std::string& str) { std::transform(std::begin(str), std::end(str), std::begin(str), charToLower); }
std::wstring GetMainPartOfName(std::wstring const& wname, uint32 declension)
{
@@ -539,7 +535,7 @@ std::wstring GetMainPartOfName(std::wstring const& wname, uint32 declension)
return wname;
}
-bool utf8ToConsole(const std::string& utf8str, std::string& conStr)
+bool utf8ToConsole(std::string_view utf8str, std::string& conStr)
{
#if AC_PLATFORM == AC_PLATFORM_WINDOWS
std::wstring wstr;
@@ -558,7 +554,7 @@ bool utf8ToConsole(const std::string& utf8str, std::string& conStr)
return true;
}
-bool consoleToUtf8(const std::string& conStr, std::string& utf8str)
+bool consoleToUtf8(std::string_view conStr, std::string& utf8str)
{
#if AC_PLATFORM == AC_PLATFORM_WINDOWS
std::wstring wstr;
@@ -573,7 +569,7 @@ bool consoleToUtf8(const std::string& conStr, std::string& utf8str)
#endif
}
-bool Utf8FitTo(const std::string& str, std::wstring const& search)
+bool Utf8FitTo(std::string_view str, std::wstring_view search)
{
std::wstring temp;
@@ -661,7 +657,7 @@ std::string Acore::Impl::ByteArrayToHexStr(uint8 const* bytes, size_t arrayLen,
return ss.str();
}
-void Acore::Impl::HexStrToByteArray(std::string const& str, uint8* out, size_t outlen, bool reverse /*= false*/)
+void Acore::Impl::HexStrToByteArray(std::string_view str, uint8* out, size_t outlen, bool reverse /*= false*/)
{
ASSERT(str.size() == (2 * outlen));
@@ -684,13 +680,23 @@ void Acore::Impl::HexStrToByteArray(std::string const& str, uint8* out, size_t o
}
}
-bool StringContainsStringI(std::string const& haystack, std::string const& needle)
+bool StringEqualI(std::string_view a, std::string_view b)
+{
+ return std::equal(a.begin(), a.end(), b.begin(), b.end(), [](char c1, char c2) { return std::tolower(c1) == std::tolower(c2); });
+}
+
+bool StringContainsStringI(std::string_view haystack, std::string_view needle)
{
return haystack.end() !=
- std::search(haystack.begin(), haystack.end(), needle.begin(), needle.end(), [](char c1, char c2) { return std::toupper(c1) == std::toupper(c2); });
+ std::search(haystack.begin(), haystack.end(), needle.begin(), needle.end(), [](char c1, char c2) { return std::tolower(c1) == std::tolower(c2); });
}
-bool StringEqualI(std::string_view a, std::string_view b)
+bool StringCompareLessI(std::string_view a, std::string_view b)
{
- return std::equal(a.begin(), a.end(), b.begin(), b.end(), [](char c1, char c2) { return std::tolower(c1) == std::tolower(c2); });
+ return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end(), [](char c1, char c2) { return std::tolower(c1) < std::tolower(c2); });
+}
+
+std::string GetTypeName(std::type_info const& info)
+{
+ return boost::core::demangle(info.name());
}
diff --git a/src/common/Utilities/Util.h b/src/common/Utilities/Util.h
index 116c67f3b5..4fbe541080 100644
--- a/src/common/Utilities/Util.h
+++ b/src/common/Utilities/Util.h
@@ -119,19 +119,20 @@ inline T RoundToInterval(T& num, T floor, T ceil)
}
// UTF8 handling
-bool Utf8toWStr(const std::string& utf8str, std::wstring& wstr);
+AC_COMMON_API bool Utf8toWStr(std::string_view utf8str, std::wstring& wstr);
// in wsize==max size of buffer, out wsize==real string size
-bool Utf8toWStr(char const* utf8str, size_t csize, wchar_t* wstr, size_t& wsize);
+AC_COMMON_API bool Utf8toWStr(char const* utf8str, size_t csize, wchar_t* wstr, size_t& wsize);
-inline bool Utf8toWStr(const std::string& utf8str, wchar_t* wstr, size_t& wsize)
+inline bool Utf8toWStr(std::string_view utf8str, wchar_t* wstr, size_t& wsize)
{
- return Utf8toWStr(utf8str.c_str(), utf8str.size(), wstr, wsize);
+ return Utf8toWStr(utf8str.data(), utf8str.size(), wstr, wsize);
}
-bool WStrToUtf8(std::wstring const& wstr, std::string& utf8str);
+AC_COMMON_API bool WStrToUtf8(std::wstring_view wstr, std::string& utf8str);
+
// size==real string size
-bool WStrToUtf8(wchar_t* wstr, size_t size, std::string& utf8str);
+AC_COMMON_API bool WStrToUtf8(wchar_t const* wstr, size_t size, std::string& utf8str);
// set string to "" if invalid utf8 sequence
size_t utf8length(std::string& utf8str);
@@ -263,7 +264,7 @@ inline bool isNumericOrSpace(wchar_t wchar)
return isNumeric(wchar) || wchar == L' ';
}
-inline bool isBasicLatinString(const std::wstring& wstr, bool numericOrSpace)
+inline bool isBasicLatinString(std::wstring_view wstr, bool numericOrSpace)
{
for (wchar_t i : wstr)
if (!isBasicLatinCharacter(i) && (!numericOrSpace || !isNumericOrSpace(i)))
@@ -273,7 +274,7 @@ inline bool isBasicLatinString(const std::wstring& wstr, bool numericOrSpace)
return true;
}
-inline bool isExtendedLatinString(const std::wstring& wstr, bool numericOrSpace)
+inline bool isExtendedLatinString(std::wstring_view wstr, bool numericOrSpace)
{
for (wchar_t i : wstr)
if (!isExtendedLatinCharacter(i) && (!numericOrSpace || !isNumericOrSpace(i)))
@@ -283,7 +284,7 @@ inline bool isExtendedLatinString(const std::wstring& wstr, bool numericOrSpace)
return true;
}
-inline bool isCyrillicString(const std::wstring& wstr, bool numericOrSpace)
+inline bool isCyrillicString(std::wstring_view wstr, bool numericOrSpace)
{
for (wchar_t i : wstr)
if (!isCyrillicCharacter(i) && (!numericOrSpace || !isNumericOrSpace(i)))
@@ -293,7 +294,7 @@ inline bool isCyrillicString(const std::wstring& wstr, bool numericOrSpace)
return true;
}
-inline bool isEastAsianString(const std::wstring& wstr, bool numericOrSpace)
+inline bool isEastAsianString(std::wstring_view wstr, bool numericOrSpace)
{
for (wchar_t i : wstr)
if (!isEastAsianCharacter(i) && (!numericOrSpace || !isNumericOrSpace(i)))
@@ -303,6 +304,9 @@ inline bool isEastAsianString(const std::wstring& wstr, bool numericOrSpace)
return true;
}
+inline char charToUpper(char c) { return std::toupper(c); }
+inline char charToLower(char c) { return std::tolower(c); }
+
inline wchar_t wcharToUpper(wchar_t wchar)
{
if (wchar >= L'a' && wchar <= L'z') // LATIN SMALL LETTER A - LATIN SMALL LETTER Z
@@ -387,24 +391,22 @@ void wstrToLower(std::wstring& str);
std::wstring GetMainPartOfName(std::wstring const& wname, uint32 declension);
-bool utf8ToConsole(const std::string& utf8str, std::string& conStr);
-bool consoleToUtf8(const std::string& conStr, std::string& utf8str);
-bool Utf8FitTo(const std::string& str, std::wstring const& search);
-void utf8printf(FILE* out, const char* str, ...);
-void vutf8printf(FILE* out, const char* str, va_list* ap);
-bool Utf8ToUpperOnlyLatin(std::string& utf8String);
+AC_COMMON_API bool utf8ToConsole(std::string_view utf8str, std::string& conStr);
+AC_COMMON_API bool consoleToUtf8(std::string_view conStr, std::string& utf8str);
+AC_COMMON_API bool Utf8FitTo(std::string_view str, std::wstring_view search);
+AC_COMMON_API void utf8printf(FILE* out, const char* str, ...);
+AC_COMMON_API void vutf8printf(FILE* out, const char* str, va_list* ap);
+AC_COMMON_API bool Utf8ToUpperOnlyLatin(std::string& utf8String);
bool IsIPAddress(char const* ipaddress);
uint32 CreatePIDFile(const std::string& filename);
uint32 GetPID();
-bool StringEqualI(std::string_view str1, std::string_view str2);
-
namespace Acore::Impl
{
- std::string ByteArrayToHexStr(uint8 const* bytes, size_t length, bool reverse = false);
- void HexStrToByteArray(std::string const& str, uint8* out, size_t outlen, bool reverse = false);
+ AC_COMMON_API std::string ByteArrayToHexStr(uint8 const* bytes, size_t length, bool reverse = false);
+ AC_COMMON_API void HexStrToByteArray(std::string_view str, uint8* out, size_t outlen, bool reverse = false);
}
template<typename Container>
@@ -414,29 +416,36 @@ std::string ByteArrayToHexStr(Container const& c, bool reverse = false)
}
template<size_t Size>
-void HexStrToByteArray(std::string const& str, std::array<uint8, Size>& buf, bool reverse = false)
+void HexStrToByteArray(std::string_view str, std::array<uint8, Size>& buf, bool reverse = false)
{
Acore::Impl::HexStrToByteArray(str, buf.data(), Size, reverse);
}
+
template<size_t Size>
-std::array<uint8, Size> HexStrToByteArray(std::string const& str, bool reverse = false)
+std::array<uint8, Size> HexStrToByteArray(std::string_view str, bool reverse = false)
{
std::array<uint8, Size> arr;
HexStrToByteArray(str, arr, reverse);
return arr;
}
-bool StringContainsStringI(std::string const& haystack, std::string const& needle);
+AC_COMMON_API bool StringEqualI(std::string_view str1, std::string_view str2);
+inline bool StringStartsWith(std::string_view haystack, std::string_view needle) { return (haystack.substr(0, needle.length()) == needle); }
+inline bool StringStartsWithI(std::string_view haystack, std::string_view needle) { return StringEqualI(haystack.substr(0, needle.length()), needle); }
+AC_COMMON_API bool StringContainsStringI(std::string_view haystack, std::string_view needle);
+
template <typename T>
-inline bool ValueContainsStringI(std::pair<T, std::string> const& haystack, std::string const& needle)
+inline bool ValueContainsStringI(std::pair<T, std::string_view> const& haystack, std::string_view needle)
{
return StringContainsStringI(haystack.second, needle);
}
-#endif
-//handler for operations on large flags
-#ifndef _FLAG96
-#define _FLAG96
+AC_COMMON_API bool StringCompareLessI(std::string_view a, std::string_view b);
+
+struct StringCompareLessI_T
+{
+ bool operator()(std::string_view a, std::string_view b) const { return StringCompareLessI(a, b); }
+};
// simple class for not-modifyable list
template <typename T>
@@ -984,10 +993,27 @@ private:
};
template<typename E>
-typename std::underlying_type<E>::type AsUnderlyingType(E enumValue)
+constexpr typename std::underlying_type<E>::type AsUnderlyingType(E enumValue)
{
static_assert(std::is_enum<E>::value, "AsUnderlyingType can only be used with enums");
return static_cast<typename std::underlying_type<E>::type>(enumValue);
}
+template<typename Ret, typename T1, typename... T>
+Ret* Coalesce(T1* first, T*... rest)
+{
+ if constexpr (sizeof...(T) > 0)
+ return (first ? static_cast<Ret*>(first) : Coalesce<Ret>(rest...));
+ else
+ return static_cast<Ret*>(first);
+}
+
+AC_COMMON_API std::string GetTypeName(std::type_info const&);
+
+template <typename T>
+std::string GetTypeName() { return GetTypeName(typeid(T)); }
+
+template <typename T>
+std::enable_if_t<!std::is_same_v<std::decay_t<T>, std::type_info>, std::string> GetTypeName(T&& v) { return GetTypeName(typeid(v)); }
+
#endif
diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp
index c214d4aa08..c46f59dad2 100644
--- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp
+++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp
@@ -639,19 +639,19 @@ bool AuctionHouseObject::BuildListAuctionItems(WorldPacket& data, Player* player
// These are found in ItemRandomSuffix.dbc and ItemRandomProperties.dbc
// even though the DBC name seems misleading
- char* const* suffix = nullptr;
+ std::array<char const*, 16> const* suffix = nullptr;
if (propRefID < 0)
{
const ItemRandomSuffixEntry* itemRandEntry = sItemRandomSuffixStore.LookupEntry(-item->GetItemRandomPropertyId());
if (itemRandEntry)
- suffix = itemRandEntry->nameSuffix;
+ suffix = &itemRandEntry->Name;
}
else
{
const ItemRandomPropertiesEntry* itemRandEntry = sItemRandomPropertiesStore.LookupEntry(item->GetItemRandomPropertyId());
if (itemRandEntry)
- suffix = itemRandEntry->nameSuffix;
+ suffix = &itemRandEntry->Name;
}
// dbc local name
@@ -660,7 +660,7 @@ bool AuctionHouseObject::BuildListAuctionItems(WorldPacket& data, Player* player
// Append the suffix (ie: of the Monkey) to the name using localization
// or default enUS if localization is invalid
name += ' ';
- name += suffix[locdbc_idx >= 0 ? locdbc_idx : LOCALE_enUS];
+ name += (*suffix)[locdbc_idx >= 0 ? locdbc_idx : LOCALE_enUS];
}
}
diff --git a/src/server/game/Battlegrounds/ArenaTeam.h b/src/server/game/Battlegrounds/ArenaTeam.h
index 1e25ffb850..6fce979c6e 100644
--- a/src/server/game/Battlegrounds/ArenaTeam.h
+++ b/src/server/game/Battlegrounds/ArenaTeam.h
@@ -90,6 +90,7 @@ ERR_ARENA_TEAM_TOO_MANY_MEMBERS_S
ERR_ARENA_TEAM_LEVEL_TOO_LOW_I
*/
+// EnumUtils: DESCRIBE THIS
enum ArenaTeamTypes
{
ARENA_TEAM_2v2 = 2,
diff --git a/src/server/game/Battlegrounds/BattlegroundMgr.h b/src/server/game/Battlegrounds/BattlegroundMgr.h
index 77e13384ef..77d6d4a0ed 100644
--- a/src/server/game/Battlegrounds/BattlegroundMgr.h
+++ b/src/server/game/Battlegrounds/BattlegroundMgr.h
@@ -42,7 +42,7 @@ struct CreateBattlegroundData
uint32 MaxPlayersPerTeam;
uint32 LevelMin;
uint32 LevelMax;
- char* BattlegroundName;
+ char const* BattlegroundName;
uint32 MapID;
float Team1StartLocX;
float Team1StartLocY;
diff --git a/src/server/game/Battlegrounds/enuminfo_ArenaTeam.cpp b/src/server/game/Battlegrounds/enuminfo_ArenaTeam.cpp
new file mode 100644
index 0000000000..da18ca8b2d
--- /dev/null
+++ b/src/server/game/Battlegrounds/enuminfo_ArenaTeam.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
+ * Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
+ * Copyright (C) 2008-2021 TrinityCore <http://www.trinitycore.org/>
+ */
+
+#include "ArenaTeam.h"
+#include "Define.h"
+#include "SmartEnum.h"
+#include <stdexcept>
+
+namespace Acore::Impl::EnumUtilsImpl
+{
+
+/******************************************************************\
+|* data for enum 'ArenaTeamTypes' in 'ArenaTeam.h' auto-generated *|
+\******************************************************************/
+template <>
+AC_API_EXPORT EnumText EnumUtils<ArenaTeamTypes>::ToString(ArenaTeamTypes value)
+{
+ switch (value)
+ {
+ case ARENA_TEAM_2v2: return { "ARENA_TEAM_2v2", "ARENA_TEAM_2v2", "" };
+ case ARENA_TEAM_3v3: return { "ARENA_TEAM_3v3", "ARENA_TEAM_3v3", "" };
+ case ARENA_TEAM_5v5: return { "ARENA_TEAM_5v5", "ARENA_TEAM_5v5", "" };
+ default: throw std::out_of_range("value");
+ }
+}
+
+template <>
+AC_API_EXPORT size_t EnumUtils<ArenaTeamTypes>::Count() { return 3; }
+
+template <>
+AC_API_EXPORT ArenaTeamTypes EnumUtils<ArenaTeamTypes>::FromIndex(size_t index)
+{
+ switch (index)
+ {
+ case 0: return ARENA_TEAM_2v2;
+ case 1: return ARENA_TEAM_3v3;
+ case 2: return ARENA_TEAM_5v5;
+ default: throw std::out_of_range("index");
+ }
+}
+
+template <>
+AC_API_EXPORT size_t EnumUtils<ArenaTeamTypes>::ToIndex(ArenaTeamTypes value)
+{
+ switch (value)
+ {
+ case ARENA_TEAM_2v2: return 0;
+ case ARENA_TEAM_3v3: return 1;
+ case ARENA_TEAM_5v5: return 2;
+ default: throw std::out_of_range("value");
+ }
+}
+}
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
diff --git a/src/server/game/Chat/Chat.h b/src/server/game/Chat/Chat.h
index 08c0d1886a..4b9f3ce215 100644
--- a/src/server/game/Chat/Chat.h
+++ b/src/server/game/Chat/Chat.h
@@ -18,6 +18,7 @@
#ifndef AZEROTHCORE_CHAT_H
#define AZEROTHCORE_CHAT_H
+#include "ChatCommand.h"
#include "SharedDefines.h"
#include "Errors.h"
#include "WorldSession.h"
@@ -33,26 +34,9 @@ class WorldObject;
struct GameTele;
-class ChatCommand
-{
- typedef bool(*pHandler)(ChatHandler*, char const*);
-
-public:
- ChatCommand(char const* name, uint32 securityLevel, bool allowConsole, pHandler handler, std::string help, std::vector<ChatCommand> childCommands = std::vector<ChatCommand>())
- : Name(ASSERT_NOTNULL(name)), SecurityLevel(securityLevel), AllowConsole(allowConsole), Handler(handler), Help(std::move(help)), ChildCommands(std::move(childCommands)) { }
-
- char const* Name;
- uint32 SecurityLevel;
- bool AllowConsole;
- pHandler Handler;
- std::string Help;
- std::vector<ChatCommand> ChildCommands;
-};
-
-class ChatHandler
+class AC_GAME_API ChatHandler
{
public:
- WorldSession* GetSession() { return m_session; }
explicit ChatHandler(WorldSession* session) : m_session(session), sentErrorMessage(false) {}
virtual ~ChatHandler() { }
@@ -68,24 +52,35 @@ public:
// function with different implementation for chat/console
virtual char const* GetAcoreString(uint32 entry) const;
- virtual void SendSysMessage(char const* str);
+ virtual void SendSysMessage(std::string_view str, bool escapeCharacters = false);
void SendSysMessage(uint32 entry);
- void PSendSysMessage(char const* format, ...) ATTR_PRINTF(2, 3);
- void PSendSysMessage(uint32 entry, ...);
- std::string PGetParseString(uint32 entry, ...) const;
- bool ParseCommands(const char* text);
+ template<typename... Args>
+ void PSendSysMessage(char const* fmt, Args&&... args)
+ {
+ SendSysMessage(Acore::StringFormat(fmt, std::forward<Args>(args)...).c_str());
+ }
- static std::vector<ChatCommand> const& getCommandTable();
+ template<typename... Args>
+ void PSendSysMessage(uint32 entry, Args&&... args)
+ {
+ SendSysMessage(PGetParseString(entry, std::forward<Args>(args)...).c_str());
+ }
- bool isValidChatMessage(const char* msg);
- void SendGlobalSysMessage(const char* str);
+ template<typename... Args>
+ std::string PGetParseString(uint32 entry, Args&&... args) const
+ {
+ return Acore::StringFormat(GetAcoreString(entry), std::forward<Args>(args)...);
+ }
+
+ bool _ParseCommands(std::string_view text);
+ virtual bool ParseCommands(std::string_view text);
- bool hasStringAbbr(const char* name, const char* part);
+ void SendGlobalSysMessage(const char* str);
// function with different implementation for chat/console
- virtual bool isAvailable(ChatCommand const& cmd) const;
+ virtual bool IsHumanReadable() const { return true; }
virtual std::string GetNameLink() const { return GetNameLink(m_session->GetPlayer()); }
virtual bool needReportToTarget(Player* chr) const;
virtual LocaleConstant GetSessionDbcLocale() const;
@@ -102,16 +97,12 @@ public:
// Returns either the selected player or self if there is no selected player
Player* getSelectedPlayerOrSelf();
- char* extractKeyFromLink(char* text, char const* linkType, char** something1 = nullptr);
- char* extractKeyFromLink(char* text, char const* const* linkTypes, int* found_idx, char** something1 = nullptr);
-
- // if args have single value then it return in arg2 and arg1 == nullptr
- void extractOptFirstArg(char* args, char** arg1, char** arg2);
- char* extractQuotedArg(char* args);
+ char* extractKeyFromLink(char* text, char const* linkType, char** something1 = nullptr);
+ char* extractKeyFromLink(char* text, char const* const* linkTypes, int* found_idx, char** something1 = nullptr);
+ char* extractQuotedArg(char* args);
uint32 extractSpellIdFromLink(char* text);
ObjectGuid::LowType extractLowGuidFromLink(char* text, HighGuid& guidHigh);
- GameTele const* extractGameTeleFromLink(char* text);
bool GetPlayerGroupAndGUIDByName(const char* cname, Player*& player, Group*& group, ObjectGuid& guid, bool offline = false);
std::string extractPlayerNameFromLink(char* text);
// select by arg (name/link) or in-game selection online/offline player
@@ -125,34 +116,31 @@ public:
Creature* GetCreatureFromPlayerMapByDbGuid(ObjectGuid::LowType lowguid);
bool HasSentErrorMessage() const { return sentErrorMessage; }
void SetSentErrorMessage(bool val) { sentErrorMessage = val; }
- static bool LoadCommandTable() { return load_command_table; }
- static void SetLoadCommandTable(bool val) { load_command_table = val; }
- bool ShowHelpForCommand(std::vector<ChatCommand> const& table, const char* cmd);
+ bool IsConsole() const { return (m_session == nullptr); }
+ Player* GetPlayer() const;
+ WorldSession* GetSession() { return m_session; }
+ bool IsAvailable(uint32 securityLevel) const;
protected:
explicit ChatHandler() : m_session(nullptr), sentErrorMessage(false) {} // for CLI subclass
- static bool SetDataForCommandInTable(std::vector<ChatCommand>& table, const char* text, uint32 securityLevel, std::string const& help, std::string const& fullcommand);
- bool ExecuteCommandInTable(std::vector<ChatCommand> const& table, const char* text, std::string const& fullcmd);
- bool ShowHelpForSubCommands(std::vector<ChatCommand> const& table, char const* cmd, char const* subcmd);
private:
WorldSession* m_session; // != nullptr for chat command call and nullptr for CLI command
// common global flag
- static bool load_command_table;
bool sentErrorMessage;
};
-class CliHandler : public ChatHandler
+class AC_GAME_API CliHandler : public ChatHandler
{
public:
- typedef void Print(void*, char const*);
- explicit CliHandler(void* callbackArg, Print* zprint) : m_callbackArg(callbackArg), m_print(zprint) {}
+ using Print = void(void*, std::string_view);
+ explicit CliHandler(void* callbackArg, Print* zprint) : m_callbackArg(callbackArg), m_print(zprint) { }
// overwrite functions
char const* GetAcoreString(uint32 entry) const override;
- bool isAvailable(ChatCommand const& cmd) const override;
- void SendSysMessage(const char* str) override;
+ void SendSysMessage(std::string_view, bool escapeCharacters) override;
+ bool ParseCommands(std::string_view str) override;
std::string GetNameLink() const override;
bool needReportToTarget(Player* chr) const override;
LocaleConstant GetSessionDbcLocale() const override;
diff --git a/src/server/game/Chat/ChatCommands/ChatCommand.cpp b/src/server/game/Chat/ChatCommands/ChatCommand.cpp
new file mode 100644
index 0000000000..ea647c8651
--- /dev/null
+++ b/src/server/game/Chat/ChatCommands/ChatCommand.cpp
@@ -0,0 +1,534 @@
+/*
+ * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ChatCommand.h"
+#include "AccountMgr.h"
+#include "Chat.h"
+#include "DBCStores.h"
+#include "DatabaseEnv.h"
+#include "Log.h"
+#include "Map.h"
+#include "Player.h"
+#include "ScriptMgr.h"
+#include "StringFormat.h"
+#include "Tokenize.h"
+#include "WorldSession.h"
+
+using ChatSubCommandMap = std::map<std::string_view, Acore::Impl::ChatCommands::ChatCommandNode, StringCompareLessI_T>;
+
+void Acore::Impl::ChatCommands::ChatCommandNode::LoadFromBuilder(ChatCommandBuilder const& builder)
+{
+ if (std::holds_alternative<ChatCommandBuilder::InvokerEntry>(builder._data))
+ {
+ ASSERT(!_invoker, "Duplicate blank sub-command.");
+ AcoreStrings help;
+ std::tie(_invoker, help, _permission) = *(std::get<ChatCommandBuilder::InvokerEntry>(builder._data));
+ if (help)
+ _help.emplace<AcoreStrings>(help);
+ }
+ else
+ LoadCommandsIntoMap(this, _subCommands, std::get<ChatCommandBuilder::SubCommandEntry>(builder._data));
+}
+
+/*static*/ void Acore::Impl::ChatCommands::ChatCommandNode::LoadCommandsIntoMap(ChatCommandNode* blank,
+ std::map<std::string_view, Acore::Impl::ChatCommands::ChatCommandNode, StringCompareLessI_T>& map, Acore::ChatCommands::ChatCommandTable const& commands)
+{
+ for (ChatCommandBuilder const& builder : commands)
+ {
+ if (builder._name.empty())
+ {
+ ASSERT(blank, "Empty name command at top level is not permitted.");
+ blank->LoadFromBuilder(builder);
+ }
+ else
+ {
+ std::vector<std::string_view> const tokens = Acore::Tokenize(builder._name, COMMAND_DELIMITER, false);
+ ASSERT(!tokens.empty(), "Invalid command name '" STRING_VIEW_FMT "'.", STRING_VIEW_FMT_ARG(builder._name));
+ ChatSubCommandMap* subMap = &map;
+ for (size_t i = 0, n = (tokens.size() - 1); i < n; ++i)
+ subMap = &((*subMap)[tokens[i]]._subCommands);
+ ((*subMap)[tokens.back()]).LoadFromBuilder(builder);
+ }
+ }
+}
+
+static ChatSubCommandMap COMMAND_MAP;
+/*static*/ ChatSubCommandMap const& Acore::Impl::ChatCommands::ChatCommandNode::GetTopLevelMap()
+{
+ if (COMMAND_MAP.empty())
+ LoadCommandMap();
+
+ return COMMAND_MAP;
+}
+/*static*/ void Acore::Impl::ChatCommands::ChatCommandNode::InvalidateCommandMap()
+{
+ COMMAND_MAP.clear();
+}
+
+/*static*/ void Acore::Impl::ChatCommands::ChatCommandNode::LoadCommandMap()
+{
+ InvalidateCommandMap();
+ LoadCommandsIntoMap(nullptr, COMMAND_MAP, sScriptMgr->GetChatCommands());
+
+ if (PreparedQueryResult result = WorldDatabase.Query(WorldDatabase.GetPreparedStatement(WORLD_SEL_COMMANDS)))
+ {
+ do
+ {
+ Field* fields = result->Fetch();
+ std::string_view const name = fields[0].GetStringView();
+ std::string_view const help = fields[2].GetStringView();
+ uint32 const secLevel = fields[1].GetUInt8();
+
+ ChatCommandNode* cmd = nullptr;
+ ChatSubCommandMap* map = &COMMAND_MAP;
+
+ for (std::string_view key : Acore::Tokenize(name, COMMAND_DELIMITER, false))
+ {
+ auto it = map->find(key);
+ if (it != map->end())
+ {
+ cmd = &it->second;
+ map = &cmd->_subCommands;
+ }
+ else
+ {
+ LOG_ERROR("sql.sql", "Table `command` contains data for non-existant command '" STRING_VIEW_FMT "'. Skipped.", STRING_VIEW_FMT_ARG(name));
+ cmd = nullptr;
+ break;
+ }
+ }
+
+ if (!cmd)
+ continue;
+
+ if (cmd->_invoker && (cmd->_permission.RequiredLevel != secLevel))
+ {
+ LOG_WARN("sql.sql", "Table `command` has permission %u for '" STRING_VIEW_FMT "' which does not match the core (%u). Overriding.",
+ secLevel, STRING_VIEW_FMT_ARG(name), cmd->_permission.RequiredLevel);
+
+ cmd->_permission.RequiredLevel = secLevel;
+ }
+
+ if (std::holds_alternative<std::string>(cmd->_help))
+ LOG_ERROR("sql.sql", "Table `command` contains duplicate data for command '" STRING_VIEW_FMT "'. Skipped.", STRING_VIEW_FMT_ARG(name));
+
+ if (std::holds_alternative<std::monostate>(cmd->_help))
+ cmd->_help.emplace<std::string>(help);
+ else
+ LOG_ERROR("sql.sql", "Table `command` contains legacy help text for command '" STRING_VIEW_FMT "', which uses `trinity_string`. Skipped.", STRING_VIEW_FMT_ARG(name));
+ } while (result->NextRow());
+ }
+
+ for (auto& [name, cmd] : COMMAND_MAP)
+ cmd.ResolveNames(std::string(name));
+}
+
+void Acore::Impl::ChatCommands::ChatCommandNode::ResolveNames(std::string name)
+{
+ if (_invoker && std::holds_alternative<std::monostate>(_help))
+ LOG_WARN("sql.sql", "Table `command` is missing help text for command '" STRING_VIEW_FMT "'.", STRING_VIEW_FMT_ARG(name));
+
+ _name = name;
+
+ for (auto& [subToken, cmd] : _subCommands)
+ {
+ std::string subName(name);
+ subName.push_back(COMMAND_DELIMITER);
+ subName.append(subToken);
+ cmd.ResolveNames(subName);
+ }
+}
+
+static void LogCommandUsage(WorldSession const& session, std::string_view cmdStr)
+{
+ if (AccountMgr::IsPlayerAccount(session.GetSecurity()))
+ return;
+
+ Player* player = session.GetPlayer();
+ ObjectGuid targetGuid = player->GetTarget();
+ uint32 areaId = player->GetAreaId();
+ uint32 zoneId = player->GetZoneId();
+ std::string areaName = "Unknown";
+ std::string zoneName = "Unknown";
+ LocaleConstant locale = sWorld->GetDefaultDbcLocale();
+
+ if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(areaId))
+ {
+ areaName = area->area_name[locale];
+ }
+
+ if (AreaTableEntry const* zone = sAreaTableStore.LookupEntry(zoneId))
+ {
+ zoneName = zone->area_name[locale];
+ }
+
+ std::string logMessage = Acore::StringFormatFmt("Command: {} [Player: {} ({}) (Account: {}) X: {} Y: {} Z: {} Map: {} ({}) Area: {} ({}) Zone: {} ({}) Selected: {} ({})]",
+ cmdStr, player->GetName(), player->GetGUID().ToString(),
+ session.GetAccountId(),
+ player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetMapId(),
+ player->FindMap() ? player->FindMap()->GetMapName() : "Unknown",
+ areaId, areaName, zoneId, zoneName,
+ (player->GetSelectedUnit()) ? player->GetSelectedUnit()->GetName() : "",
+ targetGuid.ToString());
+
+ LOG_GM(session.GetAccountId(), logMessage);
+}
+
+void Acore::Impl::ChatCommands::ChatCommandNode::SendCommandHelp(ChatHandler& handler) const
+{
+ bool const hasInvoker = IsInvokerVisible(handler);
+ if (hasInvoker)
+ {
+ if (std::holds_alternative<AcoreStrings>(_help))
+ handler.SendSysMessage(std::get<AcoreStrings>(_help));
+ else if (std::holds_alternative<std::string>(_help))
+ handler.SendSysMessage(std::get<std::string>(_help));
+ else
+ {
+ handler.PSendSysMessage(LANG_CMD_HELP_GENERIC, STRING_VIEW_FMT_ARG(_name));
+ handler.PSendSysMessage(LANG_CMD_NO_HELP_AVAILABLE, STRING_VIEW_FMT_ARG(_name));
+ }
+ }
+
+ bool header = false;
+
+ for (auto it = _subCommands.begin(); it != _subCommands.end(); ++it)
+ {
+ bool const subCommandHasSubCommand = it->second.HasVisibleSubCommands(handler);
+
+ if (!subCommandHasSubCommand && !it->second.IsInvokerVisible(handler))
+ {
+ continue;
+ }
+
+ if (!header)
+ {
+ if (!hasInvoker)
+ {
+ handler.PSendSysMessage(LANG_CMD_HELP_GENERIC, STRING_VIEW_FMT_ARG(_name));
+ }
+
+ handler.SendSysMessage(LANG_SUBCMDS_LIST);
+ header = true;
+ }
+
+ handler.PSendSysMessage(subCommandHasSubCommand ? LANG_SUBCMDS_LIST_ENTRY_ELLIPSIS : LANG_SUBCMDS_LIST_ENTRY, STRING_VIEW_FMT_ARG(it->second._name));
+ }
+}
+
+namespace Acore::Impl::ChatCommands
+{
+ struct FilteredCommandListIterator
+ {
+ public:
+ FilteredCommandListIterator(ChatSubCommandMap const& map, ChatHandler const& handler, std::string_view token)
+ : _handler{ handler }, _token{ token }, _it{ map.lower_bound(token) }, _end{ map.end() }
+ {
+ _skip();
+ }
+
+ decltype(auto) operator*() const { return _it.operator*(); }
+ decltype(auto) operator->() const { return _it.operator->(); }
+ FilteredCommandListIterator& operator++()
+ {
+ ++_it;
+ _skip();
+ return *this;
+ }
+ explicit operator bool() const { return (_it != _end); }
+ bool operator!() const { return !static_cast<bool>(*this); }
+
+ private:
+ void _skip()
+ {
+ if ((_it != _end) && !StringStartsWithI(_it->first, _token))
+ _it = _end;
+ while ((_it != _end) && !_it->second.IsVisible(_handler))
+ {
+ ++_it;
+ if ((_it != _end) && !StringStartsWithI(_it->first, _token))
+ _it = _end;
+ }
+ }
+
+ ChatHandler const& _handler;
+ std::string_view const _token;
+ ChatSubCommandMap::const_iterator _it, _end;
+
+ };
+}
+
+/*static*/ bool Acore::Impl::ChatCommands::ChatCommandNode::TryExecuteCommand(ChatHandler& handler, std::string_view cmdStr)
+{
+ ChatCommandNode const* cmd = nullptr;
+ ChatSubCommandMap const* map = &GetTopLevelMap();
+
+ while (!cmdStr.empty() && (cmdStr.front() == COMMAND_DELIMITER))
+ cmdStr.remove_prefix(1);
+
+ while (!cmdStr.empty() && (cmdStr.back() == COMMAND_DELIMITER))
+ cmdStr.remove_suffix(1);
+
+ std::string_view oldTail = cmdStr;
+ while (!oldTail.empty())
+ {
+ /* oldTail = token DELIMITER newTail */
+ auto [token, newTail] = tokenize(oldTail);
+ ASSERT(!token.empty());
+
+ FilteredCommandListIterator it1(*map, handler, token);
+ if (!it1)
+ break; /* no matching subcommands found */
+
+ if (!StringEqualI(it1->first, token))
+ { /* ok, so it1 points at a partially matching subcommand - let's see if there are others */
+ auto it2 = it1;
+ ++it2;
+
+ if (it2)
+ { /* there are multiple matching subcommands - print possibilities and return */
+ if (cmd)
+ handler.PSendSysMessage(LANG_SUBCMD_AMBIGUOUS, STRING_VIEW_FMT_ARG(cmd->_name), COMMAND_DELIMITER, STRING_VIEW_FMT_ARG(token));
+ else
+ handler.PSendSysMessage(LANG_CMD_AMBIGUOUS, STRING_VIEW_FMT_ARG(token));
+
+ handler.PSendSysMessage(it1->second.HasVisibleSubCommands(handler) ? LANG_SUBCMDS_LIST_ENTRY_ELLIPSIS : LANG_SUBCMDS_LIST_ENTRY, STRING_VIEW_FMT_ARG(it1->first));
+ do
+ {
+ handler.PSendSysMessage(it2->second.HasVisibleSubCommands(handler) ? LANG_SUBCMDS_LIST_ENTRY_ELLIPSIS : LANG_SUBCMDS_LIST_ENTRY, STRING_VIEW_FMT_ARG(it2->first));
+ } while (++it2);
+
+ return true;
+ }
+ }
+
+ /* now we matched exactly one subcommand, and it1 points to it; go down the rabbit hole */
+ cmd = &it1->second;
+ map = &cmd->_subCommands;
+
+ oldTail = newTail;
+ }
+
+ if (cmd)
+ { /* if we matched a command at some point, invoke it */
+ handler.SetSentErrorMessage(false);
+ if (cmd->IsInvokerVisible(handler) && cmd->_invoker(&handler, oldTail))
+ { /* invocation succeeded, log this */
+ if (!handler.IsConsole())
+ LogCommandUsage(*handler.GetSession(), cmdStr);
+ }
+ else if (!handler.HasSentErrorMessage())
+ { /* invocation failed, we should show usage */
+ cmd->SendCommandHelp(handler);
+ handler.SetSentErrorMessage(true);
+ }
+ return true;
+ }
+
+ return false;
+}
+
+/*static*/ void Acore::Impl::ChatCommands::ChatCommandNode::SendCommandHelpFor(ChatHandler& handler, std::string_view cmdStr)
+{
+ ChatCommandNode const* cmd = nullptr;
+ ChatSubCommandMap const* map = &GetTopLevelMap();
+
+ for (std::string_view token : Acore::Tokenize(cmdStr, COMMAND_DELIMITER, false))
+ {
+ FilteredCommandListIterator it1(*map, handler, token);
+ if (!it1)
+ { /* no matching subcommands found */
+ if (cmd)
+ {
+ cmd->SendCommandHelp(handler);
+ handler.PSendSysMessage(LANG_SUBCMD_INVALID, STRING_VIEW_FMT_ARG(cmd->_name), COMMAND_DELIMITER, STRING_VIEW_FMT_ARG(token));
+ }
+ else
+ handler.PSendSysMessage(LANG_CMD_INVALID, STRING_VIEW_FMT_ARG(token));
+ return;
+ }
+
+ if (!StringEqualI(it1->first, token))
+ { /* ok, so it1 points at a partially matching subcommand - let's see if there are others */
+ auto it2 = it1;
+ ++it2;
+
+ if (it2)
+ { /* there are multiple matching subcommands - print possibilities and return */
+ if (cmd)
+ handler.PSendSysMessage(LANG_SUBCMD_AMBIGUOUS, STRING_VIEW_FMT_ARG(cmd->_name), COMMAND_DELIMITER, STRING_VIEW_FMT_ARG(token));
+ else
+ handler.PSendSysMessage(LANG_CMD_AMBIGUOUS, STRING_VIEW_FMT_ARG(token));
+
+ handler.PSendSysMessage(it1->second.HasVisibleSubCommands(handler) ? LANG_SUBCMDS_LIST_ENTRY_ELLIPSIS : LANG_SUBCMDS_LIST_ENTRY, STRING_VIEW_FMT_ARG(it1->first));
+ do
+ {
+ handler.PSendSysMessage(it2->second.HasVisibleSubCommands(handler) ? LANG_SUBCMDS_LIST_ENTRY_ELLIPSIS : LANG_SUBCMDS_LIST_ENTRY, STRING_VIEW_FMT_ARG(it2->first));
+ } while (++it2);
+
+ return;
+ }
+ }
+
+ cmd = &it1->second;
+ map = &cmd->_subCommands;
+ }
+
+ if (cmd)
+ cmd->SendCommandHelp(handler);
+ else if (cmdStr.empty())
+ {
+ FilteredCommandListIterator it(*map, handler, "");
+ if (!it)
+ return;
+ handler.SendSysMessage(LANG_AVAILABLE_CMDS);
+ do
+ {
+ handler.PSendSysMessage(it->second.HasVisibleSubCommands(handler) ? LANG_SUBCMDS_LIST_ENTRY_ELLIPSIS : LANG_SUBCMDS_LIST_ENTRY, STRING_VIEW_FMT_ARG(it->second._name));
+ } while (++it);
+ }
+ else
+ handler.PSendSysMessage(LANG_CMD_INVALID, STRING_VIEW_FMT_ARG(cmdStr));
+}
+
+/*static*/ std::vector<std::string> Acore::Impl::ChatCommands::ChatCommandNode::GetAutoCompletionsFor(ChatHandler const& handler, std::string_view cmdStr)
+{
+ std::string path;
+ ChatCommandNode const* cmd = nullptr;
+ ChatSubCommandMap const* map = &GetTopLevelMap();
+
+ while (!cmdStr.empty() && (cmdStr.front() == COMMAND_DELIMITER))
+ cmdStr.remove_prefix(1);
+
+ while (!cmdStr.empty() && (cmdStr.back() == COMMAND_DELIMITER))
+ cmdStr.remove_suffix(1);
+
+ std::string_view oldTail = cmdStr;
+ while (!oldTail.empty())
+ {
+ /* oldTail = token DELIMITER newTail */
+ auto [token, newTail] = tokenize(oldTail);
+ ASSERT(!token.empty());
+ FilteredCommandListIterator it1(*map, handler, token);
+ if (!it1)
+ break; /* no matching subcommands found */
+
+ if (!StringEqualI(it1->first, token))
+ { /* ok, so it1 points at a partially matching subcommand - let's see if there are others */
+ auto it2 = it1;
+ ++it2;
+
+ if (it2)
+ { /* there are multiple matching subcommands - terminate here and show possibilities */
+ std::vector<std::string> vec;
+ auto possibility = ([prefix = std::string_view(path), suffix = std::string_view(newTail)](std::string_view match)
+ {
+ if (prefix.empty())
+ {
+ return Acore::StringFormat(STRING_VIEW_FMT "%c" STRING_VIEW_FMT,
+ STRING_VIEW_FMT_ARG(match), COMMAND_DELIMITER, STRING_VIEW_FMT_ARG(suffix));
+ }
+ else
+ {
+ return Acore::StringFormat(STRING_VIEW_FMT "%c" STRING_VIEW_FMT "%c" STRING_VIEW_FMT,
+ STRING_VIEW_FMT_ARG(prefix), COMMAND_DELIMITER, STRING_VIEW_FMT_ARG(match), COMMAND_DELIMITER, STRING_VIEW_FMT_ARG(suffix));
+ }
+ });
+
+ vec.emplace_back(possibility(it1->first));
+
+ do vec.emplace_back(possibility(it2->first));
+ while (++it2);
+
+ return vec;
+ }
+ }
+
+ /* now we matched exactly one subcommand, and it1 points to it; go down the rabbit hole */
+ if (path.empty())
+ path.assign(it1->first);
+ else
+ {
+ path = Acore::StringFormat(STRING_VIEW_FMT "%c" STRING_VIEW_FMT,
+ STRING_VIEW_FMT_ARG(path), COMMAND_DELIMITER, STRING_VIEW_FMT_ARG(it1->first));
+ }
+ cmd = &it1->second;
+ map = &cmd->_subCommands;
+
+ oldTail = newTail;
+ }
+
+ if (!oldTail.empty())
+ { /* there is some trailing text, leave it as is */
+ if (cmd)
+ { /* if we matched a command at some point, auto-complete it */
+ return {
+ Acore::StringFormat(STRING_VIEW_FMT "%c" STRING_VIEW_FMT,
+ STRING_VIEW_FMT_ARG(path), COMMAND_DELIMITER, STRING_VIEW_FMT_ARG(oldTail))
+ };
+ }
+ else
+ return {};
+ }
+ else
+ { /* offer all subcommands */
+ auto possibility = ([prefix = std::string_view(path)](std::string_view match)
+ {
+ if (prefix.empty())
+ return std::string(match);
+ else
+ {
+ return Acore::StringFormat(STRING_VIEW_FMT "%c" STRING_VIEW_FMT,
+ STRING_VIEW_FMT_ARG(prefix), COMMAND_DELIMITER, STRING_VIEW_FMT_ARG(match));
+ }
+ });
+
+ std::vector<std::string> vec;
+ for (FilteredCommandListIterator it(*map, handler, ""); it; ++it)
+ vec.emplace_back(possibility(it->first));
+ return vec;
+ }
+}
+
+bool Acore::Impl::ChatCommands::ChatCommandNode::IsInvokerVisible(ChatHandler const& who) const
+{
+ if (!_invoker)
+ return false;
+
+ if (who.IsConsole() && (_permission.AllowConsole == Acore::ChatCommands::Console::No))
+ return false;
+
+ if (who.IsConsole() && (_permission.AllowConsole == Acore::ChatCommands::Console::Yes))
+ return true;
+
+ return !who.IsConsole() && who.IsAvailable(_permission.RequiredLevel);
+}
+
+bool Acore::Impl::ChatCommands::ChatCommandNode::HasVisibleSubCommands(ChatHandler const& who) const
+{
+ for (auto it = _subCommands.begin(); it != _subCommands.end(); ++it)
+ if (it->second.IsVisible(who))
+ return true;
+
+ return false;
+}
+
+void Acore::ChatCommands::LoadCommandMap() { Acore::Impl::ChatCommands::ChatCommandNode::LoadCommandMap(); }
+void Acore::ChatCommands::InvalidateCommandMap() { Acore::Impl::ChatCommands::ChatCommandNode::InvalidateCommandMap(); }
+bool Acore::ChatCommands::TryExecuteCommand(ChatHandler& handler, std::string_view cmd) { return Acore::Impl::ChatCommands::ChatCommandNode::TryExecuteCommand(handler, cmd); }
+void Acore::ChatCommands::SendCommandHelpFor(ChatHandler& handler, std::string_view cmd) { Acore::Impl::ChatCommands::ChatCommandNode::SendCommandHelpFor(handler, cmd); }
+std::vector<std::string> Acore::ChatCommands::GetAutoCompletionsFor(ChatHandler const& handler, std::string_view cmd) { return Acore::Impl::ChatCommands::ChatCommandNode::GetAutoCompletionsFor(handler, cmd); }
diff --git a/src/server/game/Chat/ChatCommands/ChatCommand.h b/src/server/game/Chat/ChatCommands/ChatCommand.h
new file mode 100644
index 0000000000..7caf208817
--- /dev/null
+++ b/src/server/game/Chat/ChatCommands/ChatCommand.h
@@ -0,0 +1,280 @@
+/*
+ * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _CHATCOMMAND_H
+#define _CHATCOMMAND_H
+
+#include "advstd.h"
+#include "ChatCommandArgs.h"
+#include "ChatCommandTags.h"
+#include "Define.h"
+#include "Errors.h"
+#include "Language.h"
+#include "ObjectGuid.h"
+#include "Optional.h"
+#include "StringFormat.h"
+#include "Util.h"
+#include <cstddef>
+#include <map>
+#include <utility>
+#include <tuple>
+#include <type_traits>
+#include <variant>
+#include <vector>
+
+class ChatHandler;
+
+namespace Acore::ChatCommands
+{
+ enum class Console : bool
+ {
+ No = false,
+ Yes = true
+ };
+
+ struct ChatCommandBuilder;
+ using ChatCommandTable = std::vector<ChatCommandBuilder>;
+}
+
+namespace Acore::Impl::ChatCommands
+{
+ // forward declaration
+ // ConsumeFromOffset contains the bounds check for offset, then hands off to MultiConsumer
+ // the call stack is MultiConsumer -> ConsumeFromOffset -> MultiConsumer -> ConsumeFromOffset etc
+ // MultiConsumer goes into ArgInfo for parsing on each iteration
+ template <typename Tuple, size_t offset>
+ ChatCommandResult ConsumeFromOffset(Tuple&, ChatHandler const* handler, std::string_view args);
+
+ template <typename Tuple, typename NextType, size_t offset>
+ struct MultiConsumer
+ {
+ static ChatCommandResult TryConsumeTo(Tuple& tuple, ChatHandler const* handler, std::string_view args)
+ {
+ ChatCommandResult next = ArgInfo<NextType>::TryConsume(std::get<offset>(tuple), handler, args);
+ if (next)
+ return ConsumeFromOffset<Tuple, offset + 1>(tuple, handler, *next);
+ else
+ return next;
+ }
+ };
+
+ template <typename Tuple, typename NestedNextType, size_t offset>
+ struct MultiConsumer<Tuple, Optional<NestedNextType>, offset>
+ {
+ static ChatCommandResult TryConsumeTo(Tuple& tuple, ChatHandler const* handler, std::string_view args)
+ {
+ // try with the argument
+ auto& myArg = std::get<offset>(tuple);
+ myArg.emplace();
+
+ ChatCommandResult result1 = ArgInfo<NestedNextType>::TryConsume(myArg.value(), handler, args);
+ if (result1)
+ if ((result1 = ConsumeFromOffset<Tuple, offset + 1>(tuple, handler, *result1)))
+ return result1;
+ // try again omitting the argument
+ myArg = std::nullopt;
+ ChatCommandResult result2 = ConsumeFromOffset<Tuple, offset + 1>(tuple, handler, args);
+ if (result2)
+ return result2;
+ if (result1.HasErrorMessage() && result2.HasErrorMessage())
+ {
+ return Acore::StringFormat("%s \"%s\"\n%s \"%s\"",
+ GetAcoreString(handler, LANG_CMDPARSER_EITHER), result2.GetErrorMessage().c_str(),
+ GetAcoreString(handler, LANG_CMDPARSER_OR), result1.GetErrorMessage().c_str());
+ }
+ else if (result1.HasErrorMessage())
+ return result1;
+ else
+ return result2;
+ }
+ };
+
+ template <typename Tuple, size_t offset>
+ ChatCommandResult ConsumeFromOffset([[maybe_unused]] Tuple& tuple, [[maybe_unused]] ChatHandler const* handler, std::string_view args)
+ {
+ if constexpr (offset < std::tuple_size_v<Tuple>)
+ return MultiConsumer<Tuple, std::tuple_element_t<offset, Tuple>, offset>::TryConsumeTo(tuple, handler, args);
+ else if (!args.empty()) /* the entire string must be consumed */
+ return std::nullopt;
+ else
+ return args;
+ }
+
+ template <typename T> struct HandlerToTuple { static_assert(Acore::dependant_false_v<T>, "Invalid command handler signature"); };
+ template <typename... Ts> struct HandlerToTuple<bool(ChatHandler*, Ts...)> { using type = std::tuple<ChatHandler*, advstd::remove_cvref_t<Ts>...>; };
+ template <typename T> using TupleType = typename HandlerToTuple<T>::type;
+
+ struct CommandInvoker
+ {
+ CommandInvoker() : _wrapper(nullptr), _handler(nullptr) {}
+ template <typename TypedHandler>
+ CommandInvoker(TypedHandler& handler)
+ {
+ _wrapper = [](void* handler, ChatHandler* chatHandler, std::string_view argsStr)
+ {
+ using Tuple = TupleType<TypedHandler>;
+
+ Tuple arguments;
+ std::get<0>(arguments) = chatHandler;
+ ChatCommandResult result = ConsumeFromOffset<Tuple, 1>(arguments, chatHandler, argsStr);
+ if (result)
+ return std::apply(reinterpret_cast<TypedHandler*>(handler), std::move(arguments));
+ else
+ {
+ if (result.HasErrorMessage())
+ SendErrorMessageToHandler(chatHandler, result.GetErrorMessage());
+ return false;
+ }
+ };
+ _handler = reinterpret_cast<void*>(handler);
+ }
+ CommandInvoker(bool(&handler)(ChatHandler*, char const*))
+ {
+ _wrapper = [](void* handler, ChatHandler* chatHandler, std::string_view argsStr)
+ {
+ // make a copy of the argument string
+ // legacy handlers can destroy input strings with strtok
+ std::string argsStrCopy(argsStr);
+ return reinterpret_cast<bool(*)(ChatHandler*, char const*)>(handler)(chatHandler, argsStrCopy.c_str());
+ };
+ _handler = reinterpret_cast<void*>(handler);
+ }
+
+ explicit operator bool() const { return (_wrapper != nullptr); }
+ bool operator()(ChatHandler* chatHandler, std::string_view args) const
+ {
+ ASSERT(_wrapper && _handler);
+ return _wrapper(_handler, chatHandler, args);
+ }
+
+ private:
+ using wrapper_func = bool(void*, ChatHandler*, std::string_view);
+ wrapper_func* _wrapper;
+ void* _handler;
+ };
+
+ struct CommandPermissions
+ {
+ CommandPermissions() : RequiredLevel{}, AllowConsole{} { }
+ CommandPermissions(uint32 securityLevel, Acore::ChatCommands::Console console) : RequiredLevel{ securityLevel }, AllowConsole{ console } {}
+ uint32 RequiredLevel;
+ Acore::ChatCommands::Console AllowConsole;
+ };
+
+ class ChatCommandNode
+ {
+ friend struct FilteredCommandListIterator;
+ using ChatCommandBuilder = Acore::ChatCommands::ChatCommandBuilder;
+
+ public:
+ static void LoadCommandMap();
+ static void InvalidateCommandMap();
+ static bool TryExecuteCommand(ChatHandler& handler, std::string_view cmd);
+ static void SendCommandHelpFor(ChatHandler& handler, std::string_view cmd);
+ static std::vector<std::string> GetAutoCompletionsFor(ChatHandler const& handler, std::string_view cmd);
+
+ ChatCommandNode() : _name{}, _invoker {}, _permission{}, _help{}, _subCommands{} { }
+
+ private:
+ static std::map<std::string_view, ChatCommandNode, StringCompareLessI_T> const& GetTopLevelMap();
+ static void LoadCommandsIntoMap(ChatCommandNode* blank, std::map<std::string_view, Acore::Impl::ChatCommands::ChatCommandNode, StringCompareLessI_T>& map, Acore::ChatCommands::ChatCommandTable const& commands);
+
+ void LoadFromBuilder(ChatCommandBuilder const& builder);
+ ChatCommandNode(ChatCommandNode&& other) = default;
+
+ void ResolveNames(std::string name);
+ void SendCommandHelp(ChatHandler& handler) const;
+
+ bool IsVisible(ChatHandler const& who) const { return (IsInvokerVisible(who) || HasVisibleSubCommands(who)); }
+ bool IsInvokerVisible(ChatHandler const& who) const;
+ bool HasVisibleSubCommands(ChatHandler const& who) const;
+
+ std::string _name;
+ CommandInvoker _invoker;
+ CommandPermissions _permission;
+ std::variant<std::monostate, AcoreStrings, std::string> _help;
+ std::map<std::string_view, ChatCommandNode, StringCompareLessI_T> _subCommands;
+ };
+}
+
+namespace Acore::ChatCommands
+{
+ struct ChatCommandBuilder
+ {
+ friend class Acore::Impl::ChatCommands::ChatCommandNode;
+
+ struct InvokerEntry
+ {
+ template <typename T>
+ InvokerEntry(T& handler, AcoreStrings help, uint32 securityLevel, Acore::ChatCommands::Console allowConsole)
+ : _invoker{ handler }, _help{ help }, _permissions{ securityLevel, allowConsole } { }
+
+ InvokerEntry(InvokerEntry const&) = default;
+ InvokerEntry(InvokerEntry&&) = default;
+
+ Acore::Impl::ChatCommands::CommandInvoker _invoker;
+ AcoreStrings _help;
+ Acore::Impl::ChatCommands::CommandPermissions _permissions;
+
+ auto operator*() const { return std::tie(_invoker, _help, _permissions); }
+ };
+
+ using SubCommandEntry = std::reference_wrapper<std::vector<ChatCommandBuilder> const>;
+
+ ChatCommandBuilder(ChatCommandBuilder&&) = default;
+ ChatCommandBuilder(ChatCommandBuilder const&) = default;
+
+ template <typename TypedHandler>
+ ChatCommandBuilder(char const* name, TypedHandler& handler, AcoreStrings help, uint32 securityLevel, Acore::ChatCommands::Console allowConsole)
+ : _name{ ASSERT_NOTNULL(name) }, _data{ std::in_place_type<InvokerEntry>, handler, help, securityLevel, allowConsole } { }
+
+ template <typename TypedHandler>
+ ChatCommandBuilder(char const* name, TypedHandler& handler, uint32 securityLevel, Acore::ChatCommands::Console allowConsole)
+ : ChatCommandBuilder(name, handler, AcoreStrings(), securityLevel, allowConsole) { }
+
+ ChatCommandBuilder(char const* name, std::vector<ChatCommandBuilder> const& subCommands)
+ : _name{ ASSERT_NOTNULL(name) }, _data{ std::in_place_type<SubCommandEntry>, subCommands } { }
+
+ [[deprecated("char const* parameters to command handlers are deprecated; convert this to a typed argument handler instead")]]
+ ChatCommandBuilder(char const* name, bool(&handler)(ChatHandler*, char const*), uint32 securityLevel, Acore::ChatCommands::Console allowConsole)
+ : ChatCommandBuilder(name, handler, AcoreStrings(), securityLevel, allowConsole) { }
+
+ template <typename TypedHandler>
+ [[deprecated("you are using the old-style command format; convert this to the new format ({ name, handler (not a pointer!), permission, Console::(Yes/No) })")]]
+ ChatCommandBuilder(char const* name, uint32 securityLevel, bool console, TypedHandler* handler, char const*)
+ : ChatCommandBuilder(name, *handler, AcoreStrings(), securityLevel, static_cast<Acore::ChatCommands::Console>(console)) { }
+
+ [[deprecated("you are using the old-style command format; convert this to the new format ({ name, subCommands })")]]
+ ChatCommandBuilder(char const* name, uint32, bool, std::nullptr_t, char const*, std::vector <ChatCommandBuilder> const& sub)
+ : ChatCommandBuilder(name, sub) { }
+
+ private:
+ std::string_view _name;
+ std::variant<InvokerEntry, SubCommandEntry> _data;
+ };
+
+ AC_GAME_API void LoadCommandMap();
+ AC_GAME_API void InvalidateCommandMap();
+ AC_GAME_API bool TryExecuteCommand(ChatHandler& handler, std::string_view cmd);
+ AC_GAME_API void SendCommandHelpFor(ChatHandler& handler, std::string_view cmd);
+ AC_GAME_API std::vector<std::string> GetAutoCompletionsFor(ChatHandler const& handler, std::string_view cmd);
+}
+
+// backwards compatibility with old patches
+using ChatCommand [[deprecated("std::vector<ChatCommand> should be ChatCommandTable! (using namespace Acore::ChatCommands)")]] = Acore::ChatCommands::ChatCommandBuilder;
+
+#endif
diff --git a/src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp b/src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp
new file mode 100644
index 0000000000..1aae781a38
--- /dev/null
+++ b/src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp
@@ -0,0 +1,118 @@
+/*
+ * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ChatCommandArgs.h"
+#include "AchievementMgr.h"
+#include "ChatCommand.h"
+#include "ObjectMgr.h"
+#include "SpellMgr.h"
+#include "Util.h"
+
+using namespace Acore::ChatCommands;
+using ChatCommandResult = Acore::Impl::ChatCommands::ChatCommandResult;
+
+struct AchievementVisitor
+{
+ using value_type = AchievementEntry const*;
+ value_type operator()(Hyperlink<achievement> achData) const { return achData->Achievement; }
+ value_type operator()(uint32 achId) const { return sAchievementMgr->GetAchievement(achId); }
+};
+
+ChatCommandResult Acore::Impl::ChatCommands::ArgInfo<AchievementEntry const*>::TryConsume(AchievementEntry const*& data, ChatHandler const* handler, std::string_view args)
+{
+ Variant<Hyperlink<achievement>, uint32> val;
+ ChatCommandResult result = ArgInfo<decltype(val)>::TryConsume(val, handler, args);
+
+ if (!result || (data = val.visit(AchievementVisitor())))
+ return result;
+
+ if (uint32* id = std::get_if<uint32>(&val))
+ return FormatAcoreString(handler, LANG_CMDPARSER_ACHIEVEMENT_NO_EXIST, *id);
+
+ return std::nullopt;
+}
+
+struct GameTeleVisitor
+{
+ using value_type = GameTele const*;
+ value_type operator()(Hyperlink<tele> tele) const { return sObjectMgr->GetGameTele(tele); }
+ value_type operator()(std::string_view tele) const { return sObjectMgr->GetGameTele(tele); }
+};
+
+ChatCommandResult Acore::Impl::ChatCommands::ArgInfo<GameTele const*>::TryConsume(GameTele const*& data, ChatHandler const* handler, std::string_view args)
+{
+ Variant<Hyperlink<tele>, std::string_view> val;
+ ChatCommandResult result = ArgInfo<decltype(val)>::TryConsume(val, handler, args);
+
+ if (!result || (data = val.visit(GameTeleVisitor())))
+ return result;
+
+ if (val.holds_alternative<Hyperlink<tele>>())
+ return FormatAcoreString(handler, LANG_CMDPARSER_GAME_TELE_ID_NO_EXIST, static_cast<uint32>(std::get<Hyperlink<tele>>(val)));
+ else
+ return FormatAcoreString(handler, LANG_CMDPARSER_GAME_TELE_NO_EXIST, STRING_VIEW_FMT_ARG(std::get<std::string_view>(val)));
+}
+
+struct ItemTemplateVisitor
+{
+ using value_type = ItemTemplate const*;
+ value_type operator()(Hyperlink<item> item) const { return item->Item; }
+ value_type operator()(uint32 item) { return sObjectMgr->GetItemTemplate(item); }
+};
+
+ChatCommandResult Acore::Impl::ChatCommands::ArgInfo<ItemTemplate const*>::TryConsume(ItemTemplate const*& data, ChatHandler const* handler, std::string_view args)
+{
+ Variant<Hyperlink<item>, uint32> val;
+ ChatCommandResult result = ArgInfo<decltype(val)>::TryConsume(val, handler, args);
+
+ if (!result || (data = val.visit(ItemTemplateVisitor())))
+ return result;
+
+ if (uint32* id = std::get_if<uint32>(&val))
+ return FormatAcoreString(handler, LANG_CMDPARSER_ITEM_NO_EXIST, *id);
+
+ return std::nullopt;
+}
+
+struct SpellInfoVisitor
+{
+ using value_type = SpellInfo const*;
+ value_type operator()(Hyperlink<enchant> enchant) const { return enchant; };
+ value_type operator()(Hyperlink<glyph> glyph) const { return operator()(glyph->Glyph->SpellId); };
+ value_type operator()(Hyperlink<spell> spell) const { return *spell; }
+ value_type operator()(Hyperlink<talent> talent) const
+ {
+ return operator()(talent->Talent->RankID[talent->Rank - 1]);
+ };
+ value_type operator()(Hyperlink<trade> trade) const { return trade->Spell; };
+
+ value_type operator()(uint32 spellId) const { return sSpellMgr->GetSpellInfo(spellId); }
+};
+
+ChatCommandResult Acore::Impl::ChatCommands::ArgInfo<SpellInfo const*>::TryConsume(SpellInfo const*& data, ChatHandler const* handler, std::string_view args)
+{
+ Variant<Hyperlink<enchant>, Hyperlink<glyph>, Hyperlink<spell>, Hyperlink<talent>, Hyperlink<trade>, uint32> val;
+ ChatCommandResult result = ArgInfo<decltype(val)>::TryConsume(val, handler, args);
+
+ if (!result || (data = val.visit(SpellInfoVisitor())))
+ return result;
+
+ if (uint32* id = std::get_if<uint32>(&val))
+ return FormatAcoreString(handler, LANG_CMDPARSER_SPELL_NO_EXIST, *id);
+
+ return std::nullopt;
+}
diff --git a/src/server/game/Chat/ChatCommands/ChatCommandArgs.h b/src/server/game/Chat/ChatCommands/ChatCommandArgs.h
new file mode 100644
index 0000000000..ec83b73753
--- /dev/null
+++ b/src/server/game/Chat/ChatCommands/ChatCommandArgs.h
@@ -0,0 +1,324 @@
+/*
+ * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _CHATCOMMANDARGS_H
+#define _CHATCOMMANDARGS_H
+
+#include "ChatCommandHelpers.h"
+#include "ChatCommandTags.h"
+#include "SmartEnum.h"
+#include "StringConvert.h"
+#include "StringFormat.h"
+#include "Util.h"
+#include <charconv>
+#include <map>
+#include <string>
+#include <string_view>
+
+struct GameTele;
+
+namespace Acore::Impl::ChatCommands
+{
+
+ /************************** ARGUMENT HANDLERS *******************************************\
+ |* Define how to extract contents of a certain requested type from a string *|
+ |* Must implement the following: *|
+ |* - TryConsume: T&, ChatHandler const*, std::string_view -> ChatCommandResult *|
+ |* - on match, returns tail of the provided argument string (as std::string_view) *|
+ |* - on specific error, returns error message (as std::string&& or char const*) *|
+ |* - on generic error, returns std::nullopt (this will print command usage) *|
+ |* *|
+ |* - if a match is returned, T& should be initialized to the matched value *|
+ |* - otherwise, the state of T& is indeterminate and caller will not use it *|
+ |* *|
+ \****************************************************************************************/
+ template <typename T, typename = void>
+ struct ArgInfo { static_assert(Acore::dependant_false_v<T>, "Invalid command parameter type - see ChatCommandArgs.h for possible types"); };
+
+ // catch-all for number types
+ template <typename T>
+ struct ArgInfo<T, std::enable_if_t<std::is_integral_v<T> || std::is_floating_point_v<T>>>
+ {
+ static ChatCommandResult TryConsume(T& val, ChatHandler const* handler, std::string_view args)
+ {
+ auto [token, tail] = tokenize(args);
+ if (token.empty())
+ return std::nullopt;
+
+ if (Optional<T> v = StringTo<T>(token, 0))
+ val = *v;
+ else
+ return FormatAcoreString(handler, LANG_CMDPARSER_STRING_VALUE_INVALID, STRING_VIEW_FMT_ARG(token), GetTypeName<T>().c_str());
+
+ if constexpr (std::is_floating_point_v<T>)
+ {
+ if (!std::isfinite(val))
+ return FormatAcoreString(handler, LANG_CMDPARSER_STRING_VALUE_INVALID, STRING_VIEW_FMT_ARG(token), GetTypeName<T>().c_str());
+ }
+
+ return tail;
+ }
+ };
+
+ // string_view
+ template <>
+ struct ArgInfo<std::string_view, void>
+ {
+ static ChatCommandResult TryConsume(std::string_view& val, ChatHandler const*, std::string_view args)
+ {
+ auto [token, next] = tokenize(args);
+ if (token.empty())
+ return std::nullopt;
+ val = token;
+ return next;
+ }
+ };
+
+ // string
+ template <>
+ struct ArgInfo<std::string, void>
+ {
+ static ChatCommandResult TryConsume(std::string& val, ChatHandler const* handler, std::string_view args)
+ {
+ std::string_view view;
+ ChatCommandResult next = ArgInfo<std::string_view>::TryConsume(view, handler, args);
+ if (next)
+ val.assign(view);
+ return next;
+ }
+ };
+
+ // wstring
+ template <>
+ struct ArgInfo<std::wstring, void>
+ {
+ static ChatCommandResult TryConsume(std::wstring& val, ChatHandler const* handler, std::string_view args)
+ {
+ std::string_view utf8view;
+ ChatCommandResult next = ArgInfo<std::string_view>::TryConsume(utf8view, handler, args);
+
+ if (next)
+ {
+ if (Utf8toWStr(utf8view, val))
+ return next;
+ else
+ return GetAcoreString(handler, LANG_CMDPARSER_INVALID_UTF8);
+ }
+ else
+ return std::nullopt;
+ }
+ };
+
+ // enum
+ template <typename T>
+ struct ArgInfo<T, std::enable_if_t<std::is_enum_v<T>>>
+ {
+ using SearchMap = std::map<std::string_view, Optional<T>, StringCompareLessI_T>;
+ static SearchMap MakeSearchMap()
+ {
+ SearchMap map;
+ for (T val : EnumUtils::Iterate<T>())
+ {
+ EnumText text = EnumUtils::ToString(val);
+
+ std::string_view title(text.Title);
+ std::string_view constant(text.Constant);
+
+ auto [constantIt, constantNew] = map.try_emplace(title, val);
+ if (!constantNew)
+ constantIt->second = std::nullopt;
+
+ if (title != constant)
+ {
+ auto [titleIt, titleNew] = map.try_emplace(title, val);
+ if (!titleNew)
+ titleIt->second = std::nullopt;
+ }
+ }
+ return map;
+ }
+
+ static inline SearchMap const _map = MakeSearchMap();
+
+ static T const* Match(std::string_view s)
+ {
+ auto it = _map.lower_bound(s);
+ if (it == _map.end() || !StringStartsWithI(it->first, s)) // not a match
+ return nullptr;
+
+ if (!StringEqualI(it->first, s)) // we don't have an exact match - check if it is unique
+ {
+ auto it2 = it;
+ ++it2;
+ if ((it2 != _map.end()) && StringStartsWithI(it2->first, s)) // not unique
+ return nullptr;
+ }
+
+ if (it->second)
+ return &*it->second;
+ else
+ return nullptr;
+ }
+
+ static ChatCommandResult TryConsume(T& val, ChatHandler const* handler, std::string_view args)
+ {
+ std::string_view strVal;
+ ChatCommandResult next1 = ArgInfo<std::string_view>::TryConsume(strVal, handler, args);
+ if (next1)
+ {
+ if (T const* match = Match(strVal))
+ {
+ val = *match;
+ return next1;
+ }
+ }
+
+ // Value not found. Try to parse arg as underlying type and cast it to enum type
+ using U = std::underlying_type_t<T>;
+ U uVal = 0;
+ if (ChatCommandResult next2 = ArgInfo<U>::TryConsume(uVal, handler, args))
+ {
+ if (EnumUtils::IsValid<T>(uVal))
+ {
+ val = static_cast<T>(uVal);
+ return next2;
+ }
+ }
+
+ if (next1)
+ return FormatAcoreString(handler, LANG_CMDPARSER_STRING_VALUE_INVALID, STRING_VIEW_FMT_ARG(strVal), GetTypeName<T>().c_str());
+ else
+ return next1;
+ }
+ };
+
+ // a container tag
+ template <typename T>
+ struct ArgInfo<T, std::enable_if_t<std::is_base_of_v<ContainerTag, T>>>
+ {
+ static ChatCommandResult TryConsume(T& tag, ChatHandler const* handler, std::string_view args)
+ {
+ return tag.TryConsume(handler, args);
+ }
+ };
+
+ // non-empty vector
+ template <typename T>
+ struct ArgInfo<std::vector<T>, void>
+ {
+ static ChatCommandResult TryConsume(std::vector<T>& val, ChatHandler const* handler, std::string_view args)
+ {
+ val.clear();
+ ChatCommandResult next = ArgInfo<T>::TryConsume(val.emplace_back(), handler, args);
+
+ if (!next)
+ return next;
+
+ while (ChatCommandResult next2 = ArgInfo<T>::TryConsume(val.emplace_back(), handler, *next))
+ next = std::move(next2);
+
+ val.pop_back();
+ return next;
+ }
+ };
+
+ // fixed-size array
+ template <typename T, size_t N>
+ struct ArgInfo<std::array<T, N>, void>
+ {
+ static ChatCommandResult TryConsume(std::array<T, N>& val, ChatHandler const* handler, std::string_view args)
+ {
+ ChatCommandResult next = args;
+ for (T& t : val)
+ if (!(next = ArgInfo<T>::TryConsume(t, handler, *next)))
+ break;
+ return next;
+ }
+ };
+
+ // variant
+ template <typename... Ts>
+ struct ArgInfo<Acore::ChatCommands::Variant<Ts...>>
+ {
+ using V = std::variant<Ts...>;
+ static constexpr size_t N = std::variant_size_v<V>;
+
+ template <size_t I>
+ static ChatCommandResult TryAtIndex([[maybe_unused]] Acore::ChatCommands::Variant<Ts...>& val, [[maybe_unused]] ChatHandler const* handler, [[maybe_unused]] std::string_view args)
+ {
+ if constexpr (I < N)
+ {
+ ChatCommandResult thisResult = ArgInfo<std::variant_alternative_t<I, V>>::TryConsume(val.template emplace<I>(), handler, args);
+ if (thisResult)
+ return thisResult;
+ else
+ {
+ ChatCommandResult nestedResult = TryAtIndex<I + 1>(val, handler, args);
+ if (nestedResult || !thisResult.HasErrorMessage())
+ return nestedResult;
+ if (!nestedResult.HasErrorMessage())
+ return thisResult;
+ if (StringStartsWith(nestedResult.GetErrorMessage(), "\""))
+ return Acore::StringFormat("\"%s\"\n%s %s", thisResult.GetErrorMessage().c_str(), GetAcoreString(handler, LANG_CMDPARSER_OR), nestedResult.GetErrorMessage().c_str());
+ else
+ return Acore::StringFormat("\"%s\"\n%s \"%s\"", thisResult.GetErrorMessage().c_str(), GetAcoreString(handler, LANG_CMDPARSER_OR), nestedResult.GetErrorMessage().c_str());
+ }
+ }
+ else
+ return std::nullopt;
+ }
+
+ static ChatCommandResult TryConsume(Acore::ChatCommands::Variant<Ts...>& val, ChatHandler const* handler, std::string_view args)
+ {
+ ChatCommandResult result = TryAtIndex<0>(val, handler, args);
+ if (result.HasErrorMessage() && (result.GetErrorMessage().find('\n') != std::string::npos))
+ return Acore::StringFormat("%s %s", GetAcoreString(handler, LANG_CMDPARSER_EITHER), result.GetErrorMessage().c_str());
+ return result;
+ }
+ };
+
+ // AchievementEntry* from numeric id or link
+ template <>
+ struct AC_GAME_API ArgInfo<AchievementEntry const*>
+ {
+ static ChatCommandResult TryConsume(AchievementEntry const*&, ChatHandler const*, std::string_view);
+ };
+
+ // GameTele* from string name or link
+ template <>
+ struct AC_GAME_API ArgInfo<GameTele const*>
+ {
+ static ChatCommandResult TryConsume(GameTele const*&, ChatHandler const*, std::string_view);
+ };
+
+ // ItemTemplate* from numeric id or link
+ template <>
+ struct AC_GAME_API ArgInfo<ItemTemplate const*>
+ {
+ static ChatCommandResult TryConsume(ItemTemplate const*&, ChatHandler const*, std::string_view);
+ };
+
+ // SpellInfo const* from spell id or link
+ template <>
+ struct AC_GAME_API ArgInfo<SpellInfo const*>
+ {
+ static ChatCommandResult TryConsume(SpellInfo const*&, ChatHandler const*, std::string_view);
+ };
+
+}
+
+#endif
diff --git a/src/server/game/Chat/ChatCommands/ChatCommandHelpers.cpp b/src/server/game/Chat/ChatCommands/ChatCommandHelpers.cpp
new file mode 100644
index 0000000000..f8e0efe07e
--- /dev/null
+++ b/src/server/game/Chat/ChatCommands/ChatCommandHelpers.cpp
@@ -0,0 +1,31 @@
+/*
+ * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ChatCommandHelpers.h"
+#include "Chat.h"
+#include "ObjectMgr.h"
+
+void Acore::Impl::ChatCommands::SendErrorMessageToHandler(ChatHandler* handler, std::string_view str)
+{
+ handler->SendSysMessage(str);
+ handler->SetSentErrorMessage(true);
+}
+
+char const* Acore::Impl::ChatCommands::GetAcoreString(ChatHandler const* handler, AcoreStrings which)
+{
+ return handler->GetAcoreString(which);
+}
diff --git a/src/server/game/Chat/ChatCommands/ChatCommandHelpers.h b/src/server/game/Chat/ChatCommands/ChatCommandHelpers.h
new file mode 100644
index 0000000000..6ead517316
--- /dev/null
+++ b/src/server/game/Chat/ChatCommands/ChatCommandHelpers.h
@@ -0,0 +1,132 @@
+/*
+ * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _CHATCOMMAND_HELPERS_H_
+#define _CHATCOMMAND_HELPERS_H_
+
+#include "Define.h"
+#include "Language.h"
+#include "StringFormat.h"
+#include <optional>
+#include <string>
+#include <string_view>
+#include <type_traits>
+#include <variant>
+
+class ChatHandler;
+
+namespace Acore::Impl::ChatCommands
+{
+ /***************** HELPERS *************************\
+ |* These really aren't for outside use... *|
+ \***************************************************/
+
+ static constexpr char COMMAND_DELIMITER = ' ';
+
+ template <typename T, typename = void>
+ struct tag_base
+ {
+ using type = T;
+ };
+
+ template <typename T>
+ using tag_base_t = typename tag_base<T>::type;
+
+ struct TokenizeResult {
+ explicit operator bool() { return !token.empty(); }
+ std::string_view token;
+ std::string_view tail;
+ };
+
+ inline TokenizeResult tokenize(std::string_view args)
+ {
+ TokenizeResult result;
+ if (size_t delimPos = args.find(COMMAND_DELIMITER); delimPos != std::string_view::npos)
+ {
+ result.token = args.substr(0, delimPos);
+ if (size_t tailPos = args.find_first_not_of(COMMAND_DELIMITER, delimPos); tailPos != std::string_view::npos)
+ result.tail = args.substr(tailPos);
+ }
+ else
+ result.token = args;
+
+ return result;
+ }
+
+ template <typename T, typename... Ts>
+ struct are_all_assignable
+ {
+ static constexpr bool value = (std::is_assignable_v<T&, Ts> && ...);
+ };
+
+ template <typename... Ts>
+ struct are_all_assignable<void, Ts...>
+ {
+ static constexpr bool value = false;
+ };
+
+ template <std::size_t index, typename T1, typename... Ts>
+ struct get_nth : get_nth<index-1, Ts...> { };
+
+ template <typename T1, typename... Ts>
+ struct get_nth<0, T1, Ts...>
+ {
+ using type = T1;
+ };
+
+ template <std::size_t index, typename... Ts>
+ using get_nth_t = typename get_nth<index, Ts...>::type;
+
+ // this essentially models std::optional<std::string_view>, except it can also hold an error message
+ // it has std::string_view's bool conversion and dereference operators
+ //
+ // monostate <-> unspecified error, typically end-of-string reached or parsing failed
+ // std::string <-> specified error, typically character-not-found or invalid item link
+ // std::string_view <-> success, string_view is remaining argument string
+ struct ChatCommandResult
+ {
+ ChatCommandResult(std::nullopt_t) : _storage() {}
+ ChatCommandResult(std::string const&) = delete;
+ ChatCommandResult(std::string&& s) : _storage(std::in_place_type<std::string>, std::forward<std::string>(s)) {}
+ ChatCommandResult(char const* c) : _storage(std::in_place_type<std::string>, c) {}
+ ChatCommandResult(std::string_view s) : _storage(std::in_place_type<std::string_view>, s) {}
+
+ ChatCommandResult(ChatCommandResult const&) = delete;
+ ChatCommandResult(ChatCommandResult&&) = default;
+ ChatCommandResult& operator=(ChatCommandResult const&) = delete;
+ ChatCommandResult& operator=(ChatCommandResult&&) = default;
+
+ std::string_view operator*() const { return std::get<std::string_view>(_storage); }
+ bool IsSuccessful() const { return std::holds_alternative<std::string_view>(_storage); }
+ explicit operator bool() const { return IsSuccessful(); }
+ bool HasErrorMessage() const { return std::holds_alternative<std::string>(_storage); }
+ std::string const& GetErrorMessage() const { return std::get<std::string>(_storage); }
+
+ private:
+ std::variant<std::monostate, std::string_view, std::string> _storage;
+ };
+
+ AC_GAME_API void SendErrorMessageToHandler(ChatHandler* handler, std::string_view str);
+ AC_GAME_API char const* GetAcoreString(ChatHandler const* handler, AcoreStrings which);
+ template <typename... Ts>
+ std::string FormatAcoreString(ChatHandler const* handler, AcoreStrings which, Ts&&... args)
+ {
+ return Acore::StringFormat(GetAcoreString(handler, which), std::forward<Ts>(args)...);
+ }
+}
+
+#endif
diff --git a/src/server/game/Chat/ChatCommands/ChatCommandTags.cpp b/src/server/game/Chat/ChatCommands/ChatCommandTags.cpp
new file mode 100644
index 0000000000..f9527a570a
--- /dev/null
+++ b/src/server/game/Chat/ChatCommands/ChatCommandTags.cpp
@@ -0,0 +1,145 @@
+/*
+ * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ChatCommandTags.h"
+#include "AccountMgr.h"
+#include "Chat.h"
+#include "ChatCommandArgs.h"
+#include "ObjectAccessor.h"
+#include "ObjectMgr.h"
+#include "Player.h"
+
+using namespace Acore::Impl::ChatCommands;
+
+ChatCommandResult Acore::ChatCommands::QuotedString::TryConsume(ChatHandler const* handler, std::string_view args)
+{
+ if (args.empty())
+ return std::nullopt;
+
+ if ((args[0] != '"') && (args[0] != '\''))
+ return ArgInfo<std::string>::TryConsume(*this, handler, args);
+
+ char const QUOTE = args[0];
+ for (size_t i = 1; i < args.length(); ++i)
+ {
+ if (args[i] == QUOTE)
+ {
+ auto [remainingToken, tail] = tokenize(args.substr(i + 1));
+ if (remainingToken.empty()) // if this is not empty, then we did not consume the full token
+ return tail;
+ else
+ return std::nullopt;
+ }
+
+ if (args[i] == '\\')
+ {
+ ++i;
+ if (!(i < args.length()))
+ break;
+ }
+ std::string::push_back(args[i]);
+ }
+
+ // if we reach this, we did not find a closing quote
+ return std::nullopt;
+}
+
+ChatCommandResult Acore::ChatCommands::AccountIdentifier::TryConsume(ChatHandler const* handler, std::string_view args)
+{
+ std::string_view text;
+ ChatCommandResult next = ArgInfo<std::string_view>::TryConsume(text, handler, args);
+ if (!next)
+ return next;
+
+ // first try parsing as account name
+ _name.assign(text);
+ if (!Utf8ToUpperOnlyLatin(_name))
+ return GetAcoreString(handler, LANG_CMDPARSER_INVALID_UTF8);
+
+ _id = AccountMgr::GetId(_name);
+ if (_id) // account with name exists, we are done
+ return next;
+
+ // try parsing as account id instead
+ Optional<uint32> id = Acore::StringTo<uint32>(text, 10);
+ if (!id)
+ return FormatAcoreString(handler, LANG_CMDPARSER_ACCOUNT_NAME_NO_EXIST, STRING_VIEW_FMT_ARG(_name));
+
+ _id = *id;
+
+ if (AccountMgr::GetName(_id, _name))
+ return next;
+ else
+ return FormatAcoreString(handler, LANG_CMDPARSER_ACCOUNT_ID_NO_EXIST, _id);
+}
+
+ChatCommandResult Acore::ChatCommands::PlayerIdentifier::TryConsume(ChatHandler const* handler, std::string_view args)
+{
+ Variant<Hyperlink<player>, ObjectGuid::LowType, std::string_view> val;
+ ChatCommandResult next = ArgInfo<decltype(val)>::TryConsume(val, handler, args);
+ if (!next)
+ return next;
+
+ if (val.holds_alternative<ObjectGuid::LowType>())
+ {
+ _guid = ObjectGuid::Create<HighGuid::Player>(val.get<ObjectGuid::LowType>());
+
+ if ((_player = ObjectAccessor::FindPlayerByLowGUID(_guid.GetCounter())))
+ _name = _player->GetName();
+ else if (!sObjectMgr->GetPlayerNameByGUID(_guid.GetCounter(), _name))
+ return FormatAcoreString(handler, LANG_CMDPARSER_CHAR_GUID_NO_EXIST, _guid.ToString().c_str());
+
+ return next;
+ }
+ else
+ {
+ if (val.holds_alternative<Hyperlink<player>>())
+ _name.assign(static_cast<std::string_view>(val.get<Hyperlink<player>>()));
+ else
+ _name.assign(val.get<std::string_view>());
+
+ if (!normalizePlayerName(_name))
+ return FormatAcoreString(handler, LANG_CMDPARSER_CHAR_NAME_INVALID, STRING_VIEW_FMT_ARG(_name));
+
+ if ((_player = ObjectAccessor::FindPlayerByName(_name)))
+ _guid = _player->GetGUID();
+ else if (!(_guid = sObjectMgr->GetPlayerGUIDByName(_name)))
+ return FormatAcoreString(handler, LANG_CMDPARSER_CHAR_NAME_NO_EXIST, STRING_VIEW_FMT_ARG(_name));
+
+ return next;
+ }
+}
+
+Acore::ChatCommands::PlayerIdentifier::PlayerIdentifier(Player& player)
+ : _name(player.GetName()), _guid(player.GetGUID()), _player(&player) {}
+
+/*static*/ Optional<Acore::ChatCommands::PlayerIdentifier> Acore::ChatCommands::PlayerIdentifier::FromTarget(ChatHandler* handler)
+{
+ if (Player* player = handler->GetPlayer())
+ if (Player* target = player->GetSelectedPlayer())
+ return { *target };
+
+ return std::nullopt;
+}
+
+/*static*/ Optional<Acore::ChatCommands::PlayerIdentifier> Acore::ChatCommands::PlayerIdentifier::FromSelf(ChatHandler* handler)
+{
+ if (Player* player = handler->GetPlayer())
+ return { *player };
+
+ return std::nullopt;
+}
diff --git a/src/server/game/Chat/ChatCommands/ChatCommandTags.h b/src/server/game/Chat/ChatCommands/ChatCommandTags.h
new file mode 100644
index 0000000000..70e86c305a
--- /dev/null
+++ b/src/server/game/Chat/ChatCommands/ChatCommandTags.h
@@ -0,0 +1,313 @@
+/*
+ * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _CHATCOMMANDTAGS_H
+#define _CHATCOMMANDTAGS_H
+
+#include "advstd.h"
+#include "ChatCommandHelpers.h"
+#include "Hyperlinks.h"
+#include "ObjectGuid.h"
+#include "Optional.h"
+#include "Util.h"
+#include <boost/preprocessor/repetition/repeat.hpp>
+#include <boost/preprocessor/punctuation/comma_if.hpp>
+#include <cmath>
+#include <cstring>
+#include <iostream>
+#include <string>
+#include <string_view>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+#include <variant>
+
+class ChatHandler;
+class Player;
+
+namespace Acore::Impl::ChatCommands
+{
+ struct ContainerTag
+ {
+ using ChatCommandResult = Acore::Impl::ChatCommands::ChatCommandResult;
+ };
+
+ template <typename T>
+ struct tag_base<T, std::enable_if_t<std::is_base_of_v<ContainerTag, T>>>
+ {
+ using type = typename T::value_type;
+ };
+
+ template <size_t N>
+ inline constexpr char GetChar(char const (&s)[N], size_t i)
+ {
+ static_assert(N <= 25, "The EXACT_SEQUENCE macro can only be used with up to 25 character long literals. Specify them char-by-char (null terminated) as parameters to ExactSequence<> instead.");
+ return i >= N ? '\0' : s[i];
+ }
+
+#define CHATCOMMANDS_IMPL_SPLIT_LITERAL_EXTRACT_CHAR(z, i, strliteral) \
+ BOOST_PP_COMMA_IF(i) Acore::Impl::ChatCommands::GetChar(strliteral, i)
+
+#define CHATCOMMANDS_IMPL_SPLIT_LITERAL_CONSTRAINED(maxlen, strliteral) \
+ BOOST_PP_REPEAT(maxlen, CHATCOMMANDS_IMPL_SPLIT_LITERAL_EXTRACT_CHAR, strliteral)
+
+ // this creates always 25 elements - "abc" -> 'a', 'b', 'c', '\0', '\0', ... up to 25
+#define CHATCOMMANDS_IMPL_SPLIT_LITERAL(strliteral) CHATCOMMANDS_IMPL_SPLIT_LITERAL_CONSTRAINED(25, strliteral)
+}
+
+namespace Acore::ChatCommands
+{
+ /************************** CONTAINER TAGS **********************************************\
+ |* Simple holder classes to differentiate between extraction methods *|
+ |* Must inherit from Acore::Impl::ChatCommands::ContainerTag *|
+ |* Must implement the following: *|
+ |* - TryConsume: ChatHandler const*, std::string_view -> ChatCommandResult *|
+ |* - on match, returns tail of the provided argument string (as std::string_view) *|
+ |* - on specific error, returns error message (as std::string&& or char const*) *|
+ |* - on generic error, returns std::nullopt (this will print command usage) *|
+ |* *|
+ |* - typedef value_type of type that is contained within the tag *|
+ |* - cast operator to value_type *|
+ |* *|
+ \****************************************************************************************/
+
+ template <char... chars>
+ struct ExactSequence : Acore::Impl::ChatCommands::ContainerTag
+ {
+ using value_type = void;
+
+ ChatCommandResult TryConsume(ChatHandler const* handler, std::string_view args) const
+ {
+ if (args.empty())
+ return std::nullopt;
+ std::string_view start = args.substr(0, _string.length());
+ if (StringEqualI(start, _string))
+ {
+ auto [remainingToken, tail] = Acore::Impl::ChatCommands::tokenize(args.substr(_string.length()));
+ if (remainingToken.empty()) // if this is not empty, then we did not consume the full token
+ return tail;
+ start = args.substr(0, _string.length() + remainingToken.length());
+ }
+ return Acore::Impl::ChatCommands::FormatAcoreString(handler, LANG_CMDPARSER_EXACT_SEQ_MISMATCH, STRING_VIEW_FMT_ARG(_string), STRING_VIEW_FMT_ARG(start));
+ }
+
+ private:
+ static constexpr std::array<char, sizeof...(chars)> _storage = { chars... };
+ static_assert(!_storage.empty() && (_storage.back() == '\0'), "ExactSequence parameters must be null terminated! Use the EXACT_SEQUENCE macro to make this easier!");
+ static constexpr std::string_view _string = { _storage.data(), std::string_view::traits_type::length(_storage.data()) };
+ };
+
+#define EXACT_SEQUENCE(str) Acore::ChatCommands::ExactSequence<CHATCOMMANDS_IMPL_SPLIT_LITERAL(str)>
+
+ struct Tail : std::string_view, Acore::Impl::ChatCommands::ContainerTag
+ {
+ using value_type = std::string_view;
+
+ using std::string_view::operator=;
+
+ ChatCommandResult TryConsume(ChatHandler const*, std::string_view args)
+ {
+ std::string_view::operator=(args);
+ return std::string_view();
+ }
+ };
+
+ struct WTail : std::wstring, Acore::Impl::ChatCommands::ContainerTag
+ {
+ using value_type = std::wstring;
+
+ using std::wstring::operator=;
+
+ ChatCommandResult TryConsume(ChatHandler const* handler, std::string_view args)
+ {
+ if (Utf8toWStr(args, *this))
+ return std::string_view();
+ else
+ return Acore::Impl::ChatCommands::GetAcoreString(handler, LANG_CMDPARSER_INVALID_UTF8);
+ }
+ };
+
+ struct QuotedString : std::string, Acore::Impl::ChatCommands::ContainerTag
+ {
+ using value_type = std::string;
+
+ AC_GAME_API ChatCommandResult TryConsume(ChatHandler const* handler, std::string_view args);
+ };
+
+ struct AC_GAME_API AccountIdentifier : Acore::Impl::ChatCommands::ContainerTag
+ {
+ using value_type = uint32;
+
+ operator uint32() const { return _id; }
+ operator std::string const& () const { return _name; }
+ operator std::string_view() const { return { _name }; }
+
+ uint32 GetID() const { return _id; }
+ std::string const& GetName() const { return _name; }
+
+ ChatCommandResult TryConsume(ChatHandler const* handler, std::string_view args);
+
+ private:
+ uint32 _id;
+ std::string _name;
+ };
+
+ struct AC_GAME_API PlayerIdentifier : Acore::Impl::ChatCommands::ContainerTag
+ {
+ using value_type = Player*;
+
+ PlayerIdentifier() : _name(), _guid(), _player(nullptr) {}
+ PlayerIdentifier(Player& player);
+
+ operator ObjectGuid() const { return _guid; }
+ operator std::string const&() const { return _name; }
+ operator std::string_view() const { return _name; }
+
+ std::string const& GetName() const { return _name; }
+ ObjectGuid GetGUID() const { return _guid; }
+ bool IsConnected() const { return (_player != nullptr); }
+ Player* GetConnectedPlayer() const { return _player; }
+
+ ChatCommandResult TryConsume(ChatHandler const* handler, std::string_view args);
+
+ static Optional<PlayerIdentifier> FromTarget(ChatHandler* handler);
+ static Optional<PlayerIdentifier> FromSelf(ChatHandler* handler);
+ static Optional<PlayerIdentifier> FromTargetOrSelf(ChatHandler* handler)
+ {
+ if (Optional<PlayerIdentifier> fromTarget = FromTarget(handler))
+ return fromTarget;
+ else
+ return FromSelf(handler);
+ }
+
+ private:
+ std::string _name;
+ ObjectGuid _guid;
+ Player* _player;
+ };
+
+ template <typename linktag>
+ struct Hyperlink : Acore::Impl::ChatCommands::ContainerTag
+ {
+ using value_type = typename linktag::value_type;
+ using storage_type = advstd::remove_cvref_t<value_type>;
+
+ operator value_type() const { return val; }
+ value_type operator*() const { return val; }
+ storage_type const* operator->() const { return &val; }
+
+ ChatCommandResult TryConsume(ChatHandler const* handler, std::string_view args)
+ {
+ Acore::Hyperlinks::HyperlinkInfo info = Acore::Hyperlinks::ParseSingleHyperlink(args);
+ // invalid hyperlinks cannot be consumed
+ if (!info)
+ return std::nullopt;
+
+ // check if we got the right tag
+ if (info.tag != linktag::tag())
+ return std::nullopt;
+
+ // store value
+ if (!linktag::StoreTo(val, info.data))
+ return Acore::Impl::ChatCommands::GetAcoreString(handler, LANG_CMDPARSER_LINKDATA_INVALID);
+
+ // finally, skip any potential delimiters
+ auto [token, next] = Acore::Impl::ChatCommands::tokenize(info.tail);
+ if (token.empty()) /* empty token = first character is delimiter, skip past it */
+ return next;
+ else
+ return info.tail;
+ }
+
+ private:
+ storage_type val;
+ };
+
+ // pull in link tags for user convenience
+ using namespace ::Acore::Hyperlinks::LinkTags;
+}
+
+namespace Acore::Impl
+{
+ template <typename T>
+ struct CastToVisitor
+ {
+ template <typename U>
+ T operator()(U const& v) const { return v; }
+ };
+}
+
+namespace Acore::ChatCommands
+{
+ template <typename T1, typename... Ts>
+ struct Variant : public std::variant<T1, Ts...>
+ {
+ using base = std::variant<T1, Ts...>;
+
+ using first_type = Acore::Impl::ChatCommands::tag_base_t<T1>;
+ static constexpr bool have_operators = Acore::Impl::ChatCommands::are_all_assignable<first_type, Acore::Impl::ChatCommands::tag_base_t<Ts>...>::value;
+
+ template <bool C = have_operators>
+ std::enable_if_t<C, first_type> operator*() const
+ {
+ return visit(Acore::Impl::CastToVisitor<first_type>());
+ }
+
+ template <bool C = have_operators>
+ operator std::enable_if_t<C, first_type>() const
+ {
+ return operator*();
+ }
+
+ template<bool C = have_operators>
+ operator std::enable_if_t<C && !std::is_same_v<first_type, size_t> && std::is_convertible_v<first_type, size_t>, size_t>() const
+ {
+ return operator*();
+ }
+
+ template <bool C = have_operators>
+ std::enable_if_t<C, bool> operator!() const { return !**this; }
+
+ template <typename T>
+ Variant& operator=(T&& arg) { base::operator=(std::forward<T>(arg)); return *this; }
+
+ template <size_t index>
+ constexpr decltype(auto) get() { return std::get<index>(static_cast<base&>(*this)); }
+ template <size_t index>
+ constexpr decltype(auto) get() const { return std::get<index>(static_cast<base const&>(*this)); }
+ template <typename type>
+ constexpr decltype(auto) get() { return std::get<type>(static_cast<base&>(*this)); }
+ template <typename type>
+ constexpr decltype(auto) get() const { return std::get<type>(static_cast<base const&>(*this)); }
+
+ template <typename T>
+ constexpr decltype(auto) visit(T&& arg) { return std::visit(std::forward<T>(arg), static_cast<base&>(*this)); }
+ template <typename T>
+ constexpr decltype(auto) visit(T&& arg) const { return std::visit(std::forward<T>(arg), static_cast<base const&>(*this)); }
+
+ template <typename T>
+ constexpr bool holds_alternative() const { return std::holds_alternative<T>(static_cast<base const&>(*this)); }
+
+ template <bool C = have_operators>
+ friend std::enable_if_t<C, std::ostream&> operator<<(std::ostream& os, Acore::ChatCommands::Variant<T1, Ts...> const& v)
+ {
+ return (os << *v);
+ }
+ };
+}
+
+#endif
diff --git a/src/server/game/Chat/ChatLink.cpp b/src/server/game/Chat/ChatLink.cpp
deleted file mode 100644
index 490305a0a0..0000000000
--- a/src/server/game/Chat/ChatLink.cpp
+++ /dev/null
@@ -1,714 +0,0 @@
-/*
- * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Affero General Public License as published by the
- * Free Software Foundation; either version 3 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "ChatLink.h"
-#include "DBCStores.h"
-#include "ObjectMgr.h"
-#include "SpellInfo.h"
-#include "SpellMgr.h"
-
-// Supported shift-links (client generated and server side)
-// |color|Hachievement:achievement_id:player_guid:0:0:0:0:0:0:0:0|h[name]|h|r
-// - client, item icon shift click, not used in server currently
-// |color|Harea:area_id|h[name]|h|r
-// |color|Hcreature:creature_guid|h[name]|h|r
-// |color|Hcreature_entry:creature_id|h[name]|h|r
-// |color|Henchant:recipe_spell_id|h[prof_name: recipe_name]|h|r - client, at shift click in recipes list dialog
-// |color|Hgameevent:id|h[name]|h|r
-// |color|Hgameobject:go_guid|h[name]|h|r
-// |color|Hgameobject_entry:go_id|h[name]|h|r
-// |color|Hglyph:glyph_slot_id:glyph_prop_id|h[%s]|h|r - client, at shift click in glyphs dialog, GlyphSlot.dbc, GlyphProperties.dbc
-// |color|Hitem:item_id:perm_ench_id:gem1:gem2:gem3:0:0:0:0:reporter_level|h[name]|h|r
-// - client, item icon shift click
-// |color|Hitemset:itemset_id|h[name]|h|r
-// |color|Hplayer:name|h[name]|h|r - client, in some messages, at click copy only name instead link
-// |color|Hquest:quest_id:quest_level|h[name]|h|r - client, quest list name shift-click
-// |color|Hskill:skill_id|h[name]|h|r
-// |color|Hspell:spell_id|h[name]|h|r - client, spellbook spell icon shift-click
-// |color|Htalent:talent_id, rank|h[name]|h|r - client, talent icon shift-click
-// |color|Htaxinode:id|h[name]|h|r
-// |color|Htele:id|h[name]|h|r
-// |color|Htitle:id|h[name]|h|r
-// |color|Htrade:spell_id:cur_value:max_value:unk3int:unk3str|h[name]|h|r - client, spellbook profession icon shift-click
-
-inline bool ReadUInt32(std::istringstream& iss, uint32& res)
-{
- iss >> std::dec >> res;
- return !iss.fail() && !iss.eof();
-}
-
-inline bool ReadInt32(std::istringstream& iss, int32& res)
-{
- iss >> std::dec >> res;
- return !iss.fail() && !iss.eof();
-}
-
-inline std::string ReadSkip(std::istringstream& iss, char term)
-{
- std::string res;
- char c = iss.peek();
- while (c != term && c != '\0')
- {
- res += c;
- iss.ignore(1);
- c = iss.peek();
- }
- return res;
-}
-
-inline bool CheckDelimiter(std::istringstream& iss, char delimiter, const char* context)
-{
- (void)context; // used only with EXTRA_LOGS
- char c = iss.peek();
- if (c != delimiter)
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): invalid %s link structure ('%c' expected, '%c' found)", iss.str().c_str(), context, delimiter, c);
- return false;
- }
- iss.ignore(1);
- return true;
-}
-
-inline bool ReadHex(std::istringstream& iss, uint32& res, uint32 length)
-{
- std::istringstream::pos_type pos = iss.tellg();
- iss >> std::hex >> res;
- //uint32 size = uint32(iss.gcount());
- if (length && uint32(iss.tellg() - pos) != length)
- return false;
- return !iss.fail() && !iss.eof();
-}
-
-#define DELIMITER ':'
-#define PIPE_CHAR '|'
-
-bool ChatLink::ValidateName(char* buffer, const char* /*context*/)
-{
- _name = buffer;
- return true;
-}
-
-// |color|Hitem:item_id:perm_ench_id:gem1:gem2:gem3:0:random_property:0:reporter_level|h[name]|h|r
-// |cffa335ee|Hitem:812:0:0:0:0:0:0:0:70|h[Glowing Brightwood Staff]|h|r
-bool ItemChatLink::Initialize(std::istringstream& iss)
-{
- // Read item entry
- uint32 itemEntry = 0;
- if (!ReadUInt32(iss, itemEntry))
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading item entry", iss.str().c_str());
- return false;
- }
- // Validate item
- _item = sObjectMgr->GetItemTemplate(itemEntry);
- if (!_item)
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): got invalid itemEntry %u in |item command", iss.str().c_str(), itemEntry);
- return false;
- }
- // Validate item's color
- if (_color != ItemQualityColors[_item->Quality])
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): linked item has color %u, but user claims %u", iss.str().c_str(), ItemQualityColors[_item->Quality], _color);
- return false;
- }
- // Number of various item properties after item entry
- const uint8 propsCount = 8;
- const uint8 randomPropertyPosition = 5;
- for (uint8 index = 0; index < propsCount; ++index)
- {
- if (!CheckDelimiter(iss, DELIMITER, "item"))
- return false;
-
- int32 id = 0;
- if (!ReadInt32(iss, id))
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading item property (%u)", iss.str().c_str(), index);
- return false;
- }
- if (id && (index == randomPropertyPosition))
- {
- // Validate random property
- if (id > 0)
- {
- _property = sItemRandomPropertiesStore.LookupEntry(id);
- if (!_property)
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): got invalid item property id %u in |item command", iss.str().c_str(), id);
- return false;
- }
- }
- else if (id < 0)
- {
- _suffix = sItemRandomSuffixStore.LookupEntry(-id);
- if (!_suffix)
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): got invalid item suffix id %u in |item command", iss.str().c_str(), -id);
- return false;
- }
- }
- }
- _data[index] = id;
- }
- return true;
-}
-
-inline std::string ItemChatLink::FormatName(uint8 index, ItemLocale const* locale, char* const* suffixStrings) const
-{
- std::stringstream ss;
- if (locale == nullptr || index >= locale->Name.size())
- ss << _item->Name1;
- else
- ss << locale->Name[index];
- if (suffixStrings)
- ss << ' ' << suffixStrings[index];
- return ss.str();
-}
-
-bool ItemChatLink::ValidateName(char* buffer, const char* context)
-{
- ChatLink::ValidateName(buffer, context);
-
- char* const* suffixStrings = _suffix ? _suffix->nameSuffix : (_property ? _property->nameSuffix : nullptr);
-
- bool res = (FormatName(LOCALE_enUS, nullptr, suffixStrings) == buffer);
-
- if (!res)
- {
- ItemLocale const* il = sObjectMgr->GetItemLocale(_item->ItemId);
- for (uint8 index = LOCALE_koKR; index < TOTAL_LOCALES; ++index)
- {
- if (FormatName(index, il, suffixStrings) == buffer)
- {
- res = true;
- break;
- }
- }
- }
-
- if (!res)
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): linked item (id: %u) name wasn't found in any localization", context, _item->ItemId);
- return res;
-}
-
-// |color|Hquest:quest_id:quest_level|h[name]|h|r
-// |cff808080|Hquest:2278:47|h[The Platinum Discs]|h|r
-bool QuestChatLink::Initialize(std::istringstream& iss)
-{
- // Read quest id
- uint32 questId = 0;
- if (!ReadUInt32(iss, questId))
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading quest entry", iss.str().c_str());
- return false;
- }
- // Validate quest
- _quest = sObjectMgr->GetQuestTemplate(questId);
- if (!_quest)
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): quest template %u not found", iss.str().c_str(), questId);
- return false;
- }
- // Check delimiter
- if (!CheckDelimiter(iss, DELIMITER, "quest"))
- return false;
- // Read quest level
- if (!ReadInt32(iss, _questLevel))
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading quest level", iss.str().c_str());
- return false;
- }
- // Validate quest level
- if (_questLevel >= STRONG_MAX_LEVEL)
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): quest level %d is too big", iss.str().c_str(), _questLevel);
- return false;
- }
- return true;
-}
-
-bool QuestChatLink::ValidateName(char* buffer, const char* context)
-{
- ChatLink::ValidateName(buffer, context);
-
- bool res = (_quest->GetTitle() == buffer);
- if (!res)
- if (QuestLocale const* ql = sObjectMgr->GetQuestLocale(_quest->GetQuestId()))
- for (uint8 i = 0; i < ql->Title.size(); i++)
- if (ql->Title[i] == buffer)
- {
- res = true;
- break;
- }
-
- if (!res)
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): linked quest (id: %u) title wasn't found in any localization", context, _quest->GetQuestId());
- return res;
-}
-
-// |color|Hspell:spell_id|h[name]|h|r
-// |cff71d5ff|Hspell:21563|h[Command]|h|r
-bool SpellChatLink::Initialize(std::istringstream& iss)
-{
- if (_color != CHAT_LINK_COLOR_SPELL)
- return false;
- // Read spell id
- uint32 spellId = 0;
- if (!ReadUInt32(iss, spellId))
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading spell entry", iss.str().c_str());
- return false;
- }
- // Validate spell
- _spell = sSpellMgr->GetSpellInfo(spellId);
- if (!_spell)
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): got invalid spell id %u in |spell command", iss.str().c_str(), spellId);
- return false;
- }
- return true;
-}
-
-bool SpellChatLink::ValidateName(char* buffer, const char* context)
-{
- ChatLink::ValidateName(buffer, context);
-
- // spells with that flag have a prefix of "$PROFESSION: "
- if (_spell->HasAttribute(SPELL_ATTR0_IS_TRADESKILL))
- {
- SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(_spell->Id);
- if (bounds.first == bounds.second)
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): skill line not found for spell %u", context, _spell->Id);
- return false;
- }
- SkillLineAbilityEntry const* skillInfo = bounds.first->second;
- if (!skillInfo)
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): skill line ability not found for spell %u", context, _spell->Id);
- return false;
- }
- SkillLineEntry const* skillLine = sSkillLineStore.LookupEntry(skillInfo->SkillLine);
- if (!skillLine)
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): skill line not found for skill %u", context, skillInfo->SkillLine);
- return false;
- }
-
- for (uint8 i = 0; i < TOTAL_LOCALES; ++i)
- {
- uint32 skillLineNameLength = strlen(skillLine->name[i]);
- if (skillLineNameLength > 0 && strncmp(skillLine->name[i], buffer, skillLineNameLength) == 0)
- {
- // found the prefix, remove it to perform spellname validation below
- // -2 = strlen(": ")
- uint32 spellNameLength = (strlen(buffer) <= skillLineNameLength + 2) ? 0 : strlen(buffer) - skillLineNameLength - 2;
- memmove(buffer, buffer + skillLineNameLength + 2, spellNameLength + 1);
- break;
- }
- }
- }
-
- bool res = false;
- for (uint8 i = 0; i < TOTAL_LOCALES; ++i)
- if (*_spell->SpellName[i] && strcmp(_spell->SpellName[i], buffer) == 0)
- {
- res = true;
- break;
- }
-
- if (!res)
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): linked spell (id: %u) name wasn't found in any localization", context, _spell->Id);
- return res;
-}
-
-// |color|Hachievement:achievement_id:player_guid:0:0:0:0:0:0:0:0|h[name]|h|r
-// |cffffff00|Hachievement:546:0000000000000001:0:0:0:-1:0:0:0:0|h[Safe Deposit]|h|r
-bool AchievementChatLink::Initialize(std::istringstream& iss)
-{
- if (_color != CHAT_LINK_COLOR_ACHIEVEMENT)
- return false;
- // Read achievemnt Id
- uint32 achievementId = 0;
- if (!ReadUInt32(iss, achievementId))
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading achievement entry", iss.str().c_str());
- return false;
- }
- // Validate achievement
- _achievement = sAchievementStore.LookupEntry(achievementId);
- if (!_achievement)
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): got invalid achivement id %u in |achievement command", iss.str().c_str(), achievementId);
- return false;
- }
- // Check delimiter
- if (!CheckDelimiter(iss, DELIMITER, "achievement"))
- return false;
- // Read HEX
- if (!ReadHex(iss, _guid, 0))
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): invalid hexadecimal number while reading char's guid", iss.str().c_str());
- return false;
- }
- // Skip progress
- const uint8 propsCount = 8;
- for (uint8 index = 0; index < propsCount; ++index)
- {
- if (!CheckDelimiter(iss, DELIMITER, "achievement"))
- return false;
-
- if (!ReadUInt32(iss, _data[index]))
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading achievement property (%u)", iss.str().c_str(), index);
- return false;
- }
- }
- return true;
-}
-
-bool AchievementChatLink::ValidateName(char* buffer, const char* context)
-{
- ChatLink::ValidateName(buffer, context);
-
- bool res = false;
- for (uint8 i = 0; i < TOTAL_LOCALES; ++i)
- if (*_achievement->name[i] && strcmp(_achievement->name[i], buffer) == 0)
- {
- res = true;
- break;
- }
-
- if (!res)
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): linked achievement (id: %u) name wasn't found in any localization", context, _achievement->ID);
- return res;
-}
-
-// |color|Htrade:spell_id:cur_value:max_value:player_guid:base64_data|h[name]|h|r
-// |cffffd000|Htrade:4037:1:150:1:6AAAAAAAAAAAAAAAAAAAAAAOAADAAAAAAAAAAAAAAAAIAAAAAAAAA|h[Engineering]|h|r
-bool TradeChatLink::Initialize(std::istringstream& iss)
-{
- if (_color != CHAT_LINK_COLOR_TRADE)
- return false;
- // Spell Id
- uint32 spellId = 0;
- if (!ReadUInt32(iss, spellId))
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading achievement entry", iss.str().c_str());
- return false;
- }
- // Validate spell
- _spell = sSpellMgr->GetSpellInfo(spellId);
- if (!_spell)
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): got invalid spell id %u in |trade command", iss.str().c_str(), spellId);
- return false;
- }
- // Check delimiter
- if (!CheckDelimiter(iss, DELIMITER, "trade"))
- return false;
- // Minimum talent level
- if (!ReadInt32(iss, _minSkillLevel))
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading minimum talent level", iss.str().c_str());
- return false;
- }
- // Check delimiter
- if (!CheckDelimiter(iss, DELIMITER, "trade"))
- return false;
- // Maximum talent level
- if (!ReadInt32(iss, _maxSkillLevel))
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading maximum talent level", iss.str().c_str());
- return false;
- }
- // Check delimiter
- if (!CheckDelimiter(iss, DELIMITER, "trade"))
- return false;
- // Something hexadecimal
- if (!ReadHex(iss, _guid, 0))
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading achievement's owner guid", iss.str().c_str());
- return false;
- }
- // Skip base64 encoded stuff
- _base64 = ReadSkip(iss, PIPE_CHAR);
- return true;
-}
-
-// |color|Htalent:talent_id:rank|h[name]|h|r
-// |cff4e96f7|Htalent:2232:-1|h[Taste for Blood]|h|r
-bool TalentChatLink::Initialize(std::istringstream& iss)
-{
- if (_color != CHAT_LINK_COLOR_TALENT)
- return false;
- // Read talent entry
- if (!ReadUInt32(iss, _talentId))
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading talent entry", iss.str().c_str());
- return false;
- }
- // Validate talent
- TalentEntry const* talentInfo = sTalentStore.LookupEntry(_talentId);
- if (!talentInfo)
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): got invalid talent id %u in |talent command", iss.str().c_str(), _talentId);
- return false;
- }
- // Validate talent's spell
- _spell = sSpellMgr->GetSpellInfo(talentInfo->RankID[0]);
- if (!_spell)
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): got invalid spell id %u in |trade command", iss.str().c_str(), talentInfo->RankID[0]);
- return false;
- }
- // Delimiter
- if (!CheckDelimiter(iss, DELIMITER, "talent"))
- return false;
- // Rank
- if (!ReadInt32(iss, _rankId))
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading talent rank", iss.str().c_str());
- return false;
- }
- return true;
-}
-
-// |color|Henchant:recipe_spell_id|h[prof_name: recipe_name]|h|r
-// |cffffd000|Henchant:3919|h[Engineering: Rough Dynamite]|h|r
-bool EnchantmentChatLink::Initialize(std::istringstream& iss)
-{
- if (_color != CHAT_LINK_COLOR_ENCHANT)
- return false;
- // Spell Id
- uint32 spellId = 0;
- if (!ReadUInt32(iss, spellId))
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading enchantment spell entry", iss.str().c_str());
- return false;
- }
- // Validate spell
- _spell = sSpellMgr->GetSpellInfo(spellId);
- if (!_spell)
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): got invalid spell id %u in |enchant command", iss.str().c_str(), spellId);
- return false;
- }
- return true;
-}
-
-// |color|Hglyph:glyph_slot_id:glyph_prop_id|h[%s]|h|r
-// |cff66bbff|Hglyph:21:762|h[Glyph of Bladestorm]|h|r
-bool GlyphChatLink::Initialize(std::istringstream& iss)
-{
- if (_color != CHAT_LINK_COLOR_GLYPH)
- return false;
- // Slot
- if (!ReadUInt32(iss, _slotId))
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading slot id", iss.str().c_str());
- return false;
- }
- // Check delimiter
- if (!CheckDelimiter(iss, DELIMITER, "glyph"))
- return false;
- // Glyph Id
- uint32 glyphId = 0;
- if (!ReadUInt32(iss, glyphId))
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading glyph entry", iss.str().c_str());
- return false;
- }
- // Validate glyph
- _glyph = sGlyphPropertiesStore.LookupEntry(glyphId);
- if (!_glyph)
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): got invalid glyph id %u in |glyph command", iss.str().c_str(), glyphId);
- return false;
- }
- // Validate glyph's spell
- _spell = sSpellMgr->GetSpellInfo(_glyph->SpellId);
- if (!_spell)
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): got invalid spell id %u in |glyph command", iss.str().c_str(), _glyph->SpellId);
- return false;
- }
- return true;
-}
-
-LinkExtractor::LinkExtractor(const char* msg) : _iss(msg)
-{
-}
-
-LinkExtractor::~LinkExtractor()
-{
- for (Links::iterator itr = _links.begin(); itr != _links.end(); ++itr)
- delete *itr;
- _links.clear();
-}
-
-bool LinkExtractor::IsValidMessage()
-{
- const char validSequence[6] = "cHhhr";
- const char* validSequenceIterator = validSequence;
-
- char buffer[256];
-
- std::istringstream::pos_type startPos = 0;
- uint32 color = 0;
-
- ChatLink* link = nullptr;
- while (!_iss.eof())
- {
- if (validSequence == validSequenceIterator)
- {
- link = nullptr;
- _iss.ignore(255, PIPE_CHAR);
- startPos = _iss.tellg() - std::istringstream::pos_type(1);
- }
- else if (_iss.get() != PIPE_CHAR)
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): sequence aborted unexpectedly", _iss.str().c_str());
- return false;
- }
-
- // pipe has always to be followed by at least one char
- if (_iss.peek() == '\0')
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): pipe followed by '\\0'", _iss.str().c_str());
- return false;
- }
-
- // no further pipe commands
- if (_iss.eof())
- break;
-
- char commandChar;
- _iss.get(commandChar);
-
- // | in normal messages is escaped by ||
- if (commandChar != PIPE_CHAR)
- {
- if (commandChar == *validSequenceIterator)
- {
- if (validSequenceIterator == validSequence + 4)
- validSequenceIterator = validSequence;
- else
- ++validSequenceIterator;
- }
- else
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): invalid sequence, expected '%c' but got '%c'", _iss.str().c_str(), *validSequenceIterator, commandChar);
- return false;
- }
- }
- else if (validSequence != validSequenceIterator)
- {
- // no escaped pipes in sequences
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): got escaped pipe in sequence", _iss.str().c_str());
- return false;
- }
-
- switch (commandChar)
- {
- case 'c':
- if (!ReadHex(_iss, color, 8))
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): invalid hexadecimal number while reading color", _iss.str().c_str());
- return false;
- }
- break;
- case 'H':
- // read chars up to colon = link type
- _iss.getline(buffer, 256, DELIMITER);
- if (_iss.eof())
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly", _iss.str().c_str());
- return false;
- }
-
- if (strcmp(buffer, "item") == 0)
- link = new ItemChatLink();
- else if (strcmp(buffer, "quest") == 0)
- link = new QuestChatLink();
- else if (strcmp(buffer, "trade") == 0)
- link = new TradeChatLink();
- else if (strcmp(buffer, "talent") == 0)
- link = new TalentChatLink();
- else if (strcmp(buffer, "spell") == 0)
- link = new SpellChatLink();
- else if (strcmp(buffer, "enchant") == 0)
- link = new EnchantmentChatLink();
- else if (strcmp(buffer, "achievement") == 0)
- link = new AchievementChatLink();
- else if (strcmp(buffer, "glyph") == 0)
- link = new GlyphChatLink();
- else
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): user sent unsupported link type '%s'", _iss.str().c_str(), buffer);
- return false;
- }
- _links.push_back(link);
- link->SetColor(color);
- if (!link->Initialize(_iss))
- return false;
- break;
- case 'h':
- // if h is next element in sequence, this one must contain the linked text :)
- if (*validSequenceIterator == 'h')
- {
- // links start with '['
- if (_iss.get() != '[')
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): link caption doesn't start with '['", _iss.str().c_str());
- return false;
- }
- _iss.getline(buffer, 256, ']');
- if (_iss.eof())
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly", _iss.str().c_str());
- return false;
- }
-
- if (!link)
- return false;
-
- if (!link->ValidateName(buffer, _iss.str().c_str()))
- return false;
- }
- break;
- case 'r':
- if (link)
- link->SetBounds(startPos, _iss.tellg());
- case '|':
- // no further payload
- break;
- default:
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): got invalid command |%c", _iss.str().c_str(), commandChar);
- return false;
- }
- }
-
- // check if every opened sequence was also closed properly
- if (validSequence != validSequenceIterator)
- {
- LOG_DEBUG("chat.system", "ChatHandler::isValidChatMessage('%s'): EOF in active sequence", _iss.str().c_str());
- return false;
- }
-
- return true;
-}
diff --git a/src/server/game/Chat/ChatLink.h b/src/server/game/Chat/ChatLink.h
deleted file mode 100644
index d7f454a4a6..0000000000
--- a/src/server/game/Chat/ChatLink.h
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Affero General Public License as published by the
- * Free Software Foundation; either version 3 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef AZEROTHCORE_CHATLINK_H
-#define AZEROTHCORE_CHATLINK_H
-
-#include "SharedDefines.h"
-#include <cstring>
-#include <list>
-#include <sstream>
-
-struct ItemLocale;
-struct ItemTemplate;
-struct ItemRandomSuffixEntry;
-struct ItemRandomPropertiesEntry;
-class SpellInfo;
-struct AchievementEntry;
-struct GlyphPropertiesEntry;
-class Quest;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// ChatLink - abstract base class for various links
-class ChatLink
-{
-public:
- ChatLink() : _color(0), _startPos(0), _endPos(0) { }
- virtual ~ChatLink() { }
- void SetColor(uint32 color) { _color = color; }
- // This will allow to extract the whole link string from the message, if necessary.
- void SetBounds(std::istringstream::pos_type startPos, std::istringstream::pos_type endPos) { _startPos = startPos; _endPos = endPos; }
-
- virtual bool Initialize(std::istringstream& iss) = 0;
- virtual bool ValidateName(char* buffer, const char* context) = 0;
-
-protected:
- uint32 _color;
- std::string _name;
- std::istringstream::pos_type _startPos;
- std::istringstream::pos_type _endPos;
-};
-
-// ItemChatLink - link to item
-class ItemChatLink : public ChatLink
-{
-public:
- ItemChatLink() : ChatLink(), _item(nullptr), _suffix(nullptr), _property(nullptr)
- {
- memset(_data, 0, sizeof(_data));
- }
- bool Initialize(std::istringstream& iss) override;
- bool ValidateName(char* buffer, const char* context) override;
-
-protected:
- std::string FormatName(uint8 index, ItemLocale const* locale, char* const* suffixStrings) const;
-
- ItemTemplate const* _item;
- int32 _data[8];
- ItemRandomSuffixEntry const* _suffix;
- ItemRandomPropertiesEntry const* _property;
-};
-
-// QuestChatLink - link to quest
-class QuestChatLink : public ChatLink
-{
-public:
- QuestChatLink() : ChatLink(), _quest(nullptr), _questLevel(0) { }
- bool Initialize(std::istringstream& iss) override;
- bool ValidateName(char* buffer, const char* context) override;
-
-protected:
- Quest const* _quest;
- int32 _questLevel;
-};
-
-// SpellChatLink - link to quest
-class SpellChatLink : public ChatLink
-{
-public:
- SpellChatLink() : ChatLink(), _spell(nullptr) { }
- bool Initialize(std::istringstream& iss) override;
- bool ValidateName(char* buffer, const char* context) override;
-
-protected:
- SpellInfo const* _spell;
-};
-
-// AchievementChatLink - link to quest
-class AchievementChatLink : public ChatLink
-{
-public:
- AchievementChatLink() : ChatLink(), _guid(0), _achievement(nullptr)
- {
- memset(_data, 0, sizeof(_data));
- }
- bool Initialize(std::istringstream& iss) override;
- bool ValidateName(char* buffer, const char* context) override;
-
-protected:
- uint32 _guid;
- AchievementEntry const* _achievement;
- uint32 _data[8];
-};
-
-// TradeChatLink - link to trade info
-class TradeChatLink : public SpellChatLink
-{
-public:
- TradeChatLink() : SpellChatLink(), _minSkillLevel(0), _maxSkillLevel(0), _guid(0) { }
- bool Initialize(std::istringstream& iss) override;
-private:
- int32 _minSkillLevel;
- int32 _maxSkillLevel;
- uint32 _guid;
- std::string _base64;
-};
-
-// TalentChatLink - link to talent
-class TalentChatLink : public SpellChatLink
-{
-public:
- TalentChatLink() : SpellChatLink(), _talentId(0), _rankId(0) { }
- bool Initialize(std::istringstream& iss) override;
-
-private:
- uint32 _talentId;
- int32 _rankId;
-};
-
-// EnchantmentChatLink - link to enchantment
-class EnchantmentChatLink : public SpellChatLink
-{
-public:
- EnchantmentChatLink() : SpellChatLink() { }
- bool Initialize(std::istringstream& iss) override;
-};
-
-// GlyphChatLink - link to glyph
-class GlyphChatLink : public SpellChatLink
-{
-public:
- GlyphChatLink() : SpellChatLink(), _slotId(0), _glyph(nullptr) { }
- bool Initialize(std::istringstream& iss) override;
-private:
- uint32 _slotId;
- GlyphPropertiesEntry const* _glyph;
-};
-
-class LinkExtractor
-{
-public:
- explicit LinkExtractor(const char* msg);
- ~LinkExtractor();
-
- bool IsValidMessage();
-
-private:
- typedef std::list<ChatLink*> Links;
- Links _links;
- std::istringstream _iss;
-};
-
-#endif
diff --git a/src/server/game/Chat/HyperlinkTags.cpp b/src/server/game/Chat/HyperlinkTags.cpp
new file mode 100644
index 0000000000..9d2fe62f10
--- /dev/null
+++ b/src/server/game/Chat/HyperlinkTags.cpp
@@ -0,0 +1,261 @@
+/*
+ * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "Hyperlinks.h"
+#include "AchievementMgr.h"
+#include "ObjectMgr.h"
+#include "SpellInfo.h"
+#include "SpellMgr.h"
+#include <limits>
+
+static constexpr char HYPERLINK_DATA_DELIMITER = ':';
+
+class HyperlinkDataTokenizer
+{
+ public:
+ HyperlinkDataTokenizer(std::string_view str) : _str(str) {}
+
+ template <typename T>
+ bool TryConsumeTo(T& val)
+ {
+ if (IsEmpty())
+ return false;
+
+ if (size_t off = _str.find(HYPERLINK_DATA_DELIMITER); off != std::string_view::npos)
+ {
+ if (!Acore::Hyperlinks::LinkTags::base_tag::StoreTo(val, _str.substr(0, off)))
+ return false;
+ _str = _str.substr(off+1);
+ }
+ else
+ {
+ if (!Acore::Hyperlinks::LinkTags::base_tag::StoreTo(val, _str))
+ return false;
+ _str = std::string_view();
+ }
+
+ return true;
+ }
+
+ bool IsEmpty() { return _str.empty(); }
+
+ private:
+ std::string_view _str;
+};
+
+bool Acore::Hyperlinks::LinkTags::achievement::StoreTo(AchievementLinkData& val, std::string_view text)
+{
+ HyperlinkDataTokenizer t(text);
+ uint32 achievementId;
+
+ if (!t.TryConsumeTo(achievementId))
+ return false;
+
+ val.Achievement = sAchievementMgr->GetAchievement(achievementId);
+
+ if (!(val.Achievement && t.TryConsumeTo(val.CharacterId) && t.TryConsumeTo(val.IsFinished) && t.TryConsumeTo(val.Month) && t.TryConsumeTo(val.Day)))
+ return false;
+
+ if ((12 < val.Month) || (31 < val.Day))
+ return false;
+
+ int8 year;
+
+ if (!t.TryConsumeTo(year))
+ return false;
+
+ if (val.IsFinished) // if finished, year must be >= 0
+ {
+ if (year < 0)
+ return false;
+ val.Year = static_cast<uint8>(year);
+ }
+ else
+ val.Year = 0;
+
+ return (t.TryConsumeTo(val.Criteria[0]) && t.TryConsumeTo(val.Criteria[1]) && t.TryConsumeTo(val.Criteria[2]) && t.TryConsumeTo(val.Criteria[3]) && t.IsEmpty());
+}
+
+bool Acore::Hyperlinks::LinkTags::enchant::StoreTo(SpellInfo const*& val, std::string_view text)
+{
+ HyperlinkDataTokenizer t(text);
+ uint32 spellId;
+
+ if (!(t.TryConsumeTo(spellId) && t.IsEmpty()))
+ return false;
+
+ return (val = sSpellMgr->GetSpellInfo(spellId)) && val->HasAttribute(SPELL_ATTR0_IS_TRADESKILL);
+}
+
+bool Acore::Hyperlinks::LinkTags::glyph::StoreTo(GlyphLinkData& val, std::string_view text)
+{
+ HyperlinkDataTokenizer t(text);
+ uint32 slot, prop;
+
+ if (!(t.TryConsumeTo(slot) && t.TryConsumeTo(prop) && t.IsEmpty()))
+ return false;
+
+ if (!(val.Slot = sGlyphSlotStore.LookupEntry(slot)))
+ return false;
+
+ if (!(val.Glyph = sGlyphPropertiesStore.LookupEntry(prop)))
+ return false;
+
+ return true;
+}
+
+bool Acore::Hyperlinks::LinkTags::item::StoreTo(ItemLinkData& val, std::string_view text)
+{
+ HyperlinkDataTokenizer t(text);
+ uint32 itemId, dummy;
+
+ if (!t.TryConsumeTo(itemId))
+ return false;
+
+ val.Item = sObjectMgr->GetItemTemplate(itemId);
+ val.IsBuggedInspectLink = false;
+
+ // randomPropertyId is actually a int16 in the client
+ // positive values index ItemRandomSuffix.dbc, while negative values index ItemRandomProperties.dbc
+ // however, there is also a client bug in inspect packet handling that causes a int16 to be cast to uint16, then int32 (dropping sign extension along the way)
+ // this results in the wrong value being sent in the link; DBC lookup clientside fails, so it sends the link without suffix
+ // to detect and allow these invalid links, we first read randomPropertyId as a full int32
+ int32 randomPropertyId;
+ if (!(val.Item && t.TryConsumeTo(val.EnchantId) && t.TryConsumeTo(val.GemEnchantId[0]) && t.TryConsumeTo(val.GemEnchantId[1]) &&
+ t.TryConsumeTo(val.GemEnchantId[2]) && t.TryConsumeTo(dummy) && t.TryConsumeTo(randomPropertyId) && t.TryConsumeTo(val.RandomSuffixBaseAmount) &&
+ t.TryConsumeTo(val.RenderLevel) && t.IsEmpty() && !dummy))
+ return false;
+
+ if ((static_cast<int32>(std::numeric_limits<int16>::max()) < randomPropertyId) && (randomPropertyId <= std::numeric_limits<uint16>::max()))
+ { // this is the bug case, the id we received is actually static_cast<uint16>(i16RandomPropertyId)
+ randomPropertyId = static_cast<int16>(randomPropertyId);
+ val.IsBuggedInspectLink = true;
+ }
+
+ if (randomPropertyId < 0)
+ {
+ if (!val.Item->RandomSuffix)
+ return false;
+
+ if (randomPropertyId < -static_cast<int32>(sItemRandomSuffixStore.GetNumRows()))
+ return false;
+
+ if (ItemRandomSuffixEntry const* suffixEntry = sItemRandomSuffixStore.LookupEntry(-randomPropertyId))
+ {
+ val.RandomSuffix = suffixEntry;
+ val.RandomProperty = nullptr;
+ }
+ else
+ return false;
+ }
+ else if (randomPropertyId > 0)
+ {
+ if (!val.Item->RandomProperty)
+ return false;
+
+ if (ItemRandomPropertiesEntry const* propEntry = sItemRandomPropertiesStore.LookupEntry(randomPropertyId))
+ {
+ val.RandomSuffix = nullptr;
+ val.RandomProperty = propEntry;
+ }
+ else
+ return false;
+ }
+ else
+ {
+ val.RandomSuffix = nullptr;
+ val.RandomProperty = nullptr;
+ }
+
+ if ((val.RandomSuffix && !val.RandomSuffixBaseAmount) || (val.RandomSuffixBaseAmount && !val.RandomSuffix))
+ return false;
+
+ return true;
+}
+
+bool Acore::Hyperlinks::LinkTags::quest::StoreTo(QuestLinkData& val, std::string_view text)
+{
+ HyperlinkDataTokenizer t(text);
+ uint32 questId;
+
+ if (!t.TryConsumeTo(questId))
+ return false;
+
+ return (val.Quest = sObjectMgr->GetQuestTemplate(questId)) && t.TryConsumeTo(val.QuestLevel) && (val.QuestLevel >= -1) && t.IsEmpty();
+}
+
+bool Acore::Hyperlinks::LinkTags::spell::StoreTo(SpellInfo const*& val, std::string_view text)
+{
+ HyperlinkDataTokenizer t(text);
+ uint32 spellId;
+
+ if (!(t.TryConsumeTo(spellId) && t.IsEmpty()))
+ return false;
+
+ return !!(val = sSpellMgr->GetSpellInfo(spellId));
+}
+
+bool Acore::Hyperlinks::LinkTags::talent::StoreTo(TalentLinkData& val, std::string_view text)
+{
+ HyperlinkDataTokenizer t(text);
+ uint32 talentId;
+ int8 rank; // talent links contain <learned rank>-1, we store <learned rank>
+
+ if (!(t.TryConsumeTo(talentId) && t.TryConsumeTo(rank) && t.IsEmpty()))
+ return false;
+
+ if (rank < -1 || rank >= MAX_TALENT_RANK)
+ return false;
+
+ val.Talent = sTalentStore.LookupEntry(talentId);
+ val.Rank = rank+1;
+
+ if (!val.Talent)
+ return false;
+
+ if (val.Rank > 0)
+ {
+ uint32 const spellId = val.Talent->RankID[val.Rank - 1];
+ if (!spellId)
+ return false;
+
+ val.Spell = sSpellMgr->GetSpellInfo(spellId);
+
+ if (!val.Spell)
+ return false;
+ }
+ else
+ {
+ val.Spell = nullptr;
+ }
+
+ return true;
+}
+
+bool Acore::Hyperlinks::LinkTags::trade::StoreTo(TradeskillLinkData& val, std::string_view text)
+{
+ HyperlinkDataTokenizer t(text);
+ uint32 spellId;
+
+ if (!t.TryConsumeTo(spellId))
+ return false;
+
+ val.Spell = sSpellMgr->GetSpellInfo(spellId);
+
+ return (val.Spell && val.Spell->Effects[0].Effect == SPELL_EFFECT_TRADE_SKILL && t.TryConsumeTo(val.CurValue) &&
+ t.TryConsumeTo(val.MaxValue) && t.TryConsumeTo(val.Owner) && t.TryConsumeTo(val.KnownRecipes) && t.IsEmpty());
+}
diff --git a/src/server/game/Chat/Hyperlinks.cpp b/src/server/game/Chat/Hyperlinks.cpp
new file mode 100644
index 0000000000..b296242961
--- /dev/null
+++ b/src/server/game/Chat/Hyperlinks.cpp
@@ -0,0 +1,424 @@
+/*
+ * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "Hyperlinks.h"
+#include "advstd.h"
+#include "Common.h"
+#include "DBCStores.h"
+#include "Errors.h"
+#include "ObjectMgr.h"
+#include "SharedDefines.h"
+#include "SpellInfo.h"
+#include "SpellMgr.h"
+#include "QuestDef.h"
+
+using namespace Acore::Hyperlinks;
+
+inline uint8 toHex(char c) { return (c >= '0' && c <= '9') ? c - '0' + 0x10 : (c >= 'a' && c <= 'f') ? c - 'a' + 0x1a : 0x00; }
+
+// Validates a single hyperlink
+HyperlinkInfo Acore::Hyperlinks::ParseSingleHyperlink(std::string_view str)
+{
+ uint32 color = 0;
+ std::string_view tag;
+ std::string_view data;
+ std::string_view text;
+
+ //color tag
+ if (str.substr(0, 2) != "|c")
+ return {};
+
+ str.remove_prefix(2);
+
+ if (str.length() < 8)
+ return {};
+
+ for (uint8 i = 0; i < 8; ++i)
+ {
+ if (uint8 hex = toHex(str[i]))
+ color = (color << 4) | (hex & 0xf);
+ else
+ return {};
+ }
+
+ str.remove_prefix(8);
+
+ if (str.substr(0, 2) != "|H")
+ return {};
+
+ str.remove_prefix(2);
+
+ // tag+data part follows
+ if (size_t delimPos = str.find('|'); delimPos != std::string_view::npos)
+ {
+ tag = str.substr(0, delimPos);
+ str.remove_prefix(delimPos+1);
+ }
+ else
+ return {};
+
+ // split tag if : is present (data separator)
+ if (size_t dataStart = tag.find(':'); dataStart != std::string_view::npos)
+ {
+ data = tag.substr(dataStart+1);
+ tag = tag.substr(0, dataStart);
+ }
+
+ // ok, next should be link data end tag...
+ if (str.substr(0, 1) != "h")
+ return {};
+ str.remove_prefix(1);
+ // skip to final |
+ if (size_t end = str.find('|'); end != std::string_view::npos)
+ {
+ // check end tag
+ if (str.substr(end, 4) != "|h|r")
+ return {};
+ // check text brackets
+ if ((str[0] != '[') || (str[end - 1] != ']'))
+ return {};
+ text = str.substr(1, end - 2);
+ // tail
+ str = str.substr(end + 4);
+ }
+ else
+ return {};
+
+ // ok, valid hyperlink, return info
+ return { str, color, tag, data, text };
+}
+
+template <typename T>
+struct LinkValidator
+{
+ static bool IsTextValid(typename T::value_type, std::string_view) { return true; }
+ static bool IsColorValid(typename T::value_type, HyperlinkColor) { return true; }
+};
+
+template <>
+struct LinkValidator<LinkTags::achievement>
+{
+ static bool IsTextValid(AchievementLinkData const& data, std::string_view text)
+ {
+ if (text.empty())
+ return false;
+
+ for (uint8 i = 0; i < TOTAL_LOCALES; ++i)
+ if (text == data.Achievement->name[i])
+ return true;
+
+ return false;
+ }
+
+ static bool IsColorValid(AchievementLinkData const&, HyperlinkColor c)
+ {
+ return c == CHAT_LINK_COLOR_ACHIEVEMENT;
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::item>
+{
+ static bool IsTextValid(ItemLinkData const& data, std::string_view text)
+ {
+ ItemLocale const* locale = sObjectMgr->GetItemLocale(data.Item->ItemId);
+
+ std::array<char const*, 16> const* randomSuffixes = nullptr;
+
+ if (data.RandomProperty)
+ randomSuffixes = &data.RandomProperty->Name;
+ else if (data.RandomSuffix)
+ randomSuffixes = &data.RandomSuffix->Name;
+
+ if (data.IsBuggedInspectLink) /* DBC lookup will have failed on the client, so the link should've arrived without suffix */
+ randomSuffixes = nullptr;
+
+ for (uint8 i = 0; i < TOTAL_LOCALES; ++i)
+ {
+ if (!locale && i != DEFAULT_LOCALE)
+ continue;
+
+ std::string_view name = (i == DEFAULT_LOCALE) ? data.Item->Name1 : ObjectMgr::GetLocaleString(locale->Name, i);
+ if (name.empty())
+ continue;
+
+ if (randomSuffixes)
+ {
+ std::string_view randomSuffix((*randomSuffixes)[i]);
+ if ((!randomSuffix.empty()) &&
+ (text.length() == (name.length() + 1 + randomSuffix.length())) &&
+ (text.substr(0, name.length()) == name) &&
+ (text[name.length()] == ' ') &&
+ (text.substr(name.length() + 1) == randomSuffix))
+ return true;
+ }
+ else if (text == name)
+ return true;
+ }
+ return false;
+ }
+
+ static bool IsColorValid(ItemLinkData const& data, HyperlinkColor c)
+ {
+ return c == ItemQualityColors[data.Item->Quality];
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::quest>
+{
+ static bool IsTextValid(QuestLinkData const& data, std::string_view text)
+ {
+ if (text.empty())
+ return false;
+
+ if (text == data.Quest->GetTitle())
+ return true;
+
+ QuestLocale const* locale = sObjectMgr->GetQuestLocale(data.Quest->GetQuestId());
+ if (!locale)
+ return false;
+
+ for (uint8 i = 0; i < TOTAL_LOCALES; ++i)
+ {
+ if (i == DEFAULT_LOCALE)
+ continue;
+
+ std::string_view name = ObjectMgr::GetLocaleString(locale->Title, i);
+ if (!name.empty() && (text == name))
+ return true;
+ }
+
+ return false;
+ }
+
+ static bool IsColorValid(QuestLinkData const&, HyperlinkColor c)
+ {
+ for (uint8 i = 0; i < MAX_QUEST_DIFFICULTY; ++i)
+ if (c == QuestDifficultyColors[i])
+ return true;
+
+ return false;
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::spell>
+{
+ static bool IsTextValid(SpellInfo const* info, std::string_view text)
+ {
+ for (uint8 i = 0; i < TOTAL_LOCALES; ++i)
+ if (text == info->SpellName[i])
+ return true;
+ return false;
+ }
+
+ static bool IsColorValid(SpellInfo const*, HyperlinkColor c)
+ {
+ return c == CHAT_LINK_COLOR_SPELL;
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::enchant>
+{
+ static bool IsTextValid(SpellInfo const* info, std::string_view text)
+ {
+ if (LinkValidator<LinkTags::spell>::IsTextValid(info, text))
+ return true;
+
+ SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(info->Id);
+ if (bounds.first == bounds.second)
+ return false;
+
+ for (auto pair = bounds.first; pair != bounds.second; ++pair)
+ {
+ SkillLineEntry const* skill = sSkillLineStore.LookupEntry(pair->second->SkillLine);
+ if (!skill)
+ return false;
+
+ for (uint8 i = 0; i < TOTAL_LOCALES; ++i)
+ {
+ std::string_view skillName = skill->name[i];
+ std::string_view spellName = info->SpellName[i];
+ // alternate form [Skill Name: Spell Name]
+ if ((text.length() == (skillName.length() + 2 + spellName.length())) &&
+ (text.substr(0, skillName.length()) == skillName) &&
+ (text.substr(skillName.length(), 2) == ": ") &&
+ (text.substr(skillName.length() + 2) == spellName))
+ return true;
+ }
+ }
+ return false;
+ }
+
+ static bool IsColorValid(SpellInfo const*, HyperlinkColor c)
+ {
+ return c == CHAT_LINK_COLOR_ENCHANT;
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::glyph>
+{
+ static bool IsTextValid(GlyphLinkData const& data, std::string_view text)
+ {
+ if (SpellInfo const* info = sSpellMgr->GetSpellInfo(data.Glyph->SpellId))
+ return LinkValidator<LinkTags::spell>::IsTextValid(info, text);
+
+ return false;
+ }
+
+ static bool IsColorValid(GlyphLinkData const&, HyperlinkColor c)
+ {
+ return c == CHAT_LINK_COLOR_GLYPH;
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::talent>
+{
+ static bool IsTextValid(TalentLinkData const& data, std::string_view text)
+ {
+ SpellInfo const* info = data.Spell;
+ if (!info)
+ info = sSpellMgr->GetSpellInfo(data.Talent->RankID[0]);
+
+ if (!info)
+ return false;
+
+ return LinkValidator<LinkTags::spell>::IsTextValid(info, text);
+ }
+
+ static bool IsColorValid(TalentLinkData const&, HyperlinkColor c)
+ {
+ return c == CHAT_LINK_COLOR_TALENT;
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::trade>
+{
+ static bool IsTextValid(TradeskillLinkData const& data, std::string_view text)
+ {
+ return LinkValidator<LinkTags::spell>::IsTextValid(data.Spell, text);
+ }
+
+ static bool IsColorValid(TradeskillLinkData const&, HyperlinkColor c)
+ {
+ return c == CHAT_LINK_COLOR_TRADE;
+ }
+};
+
+template <typename TAG>
+static bool ValidateAs(HyperlinkInfo const& info)
+{
+ std::decay_t<typename TAG::value_type> t;
+ if (!TAG::StoreTo(t, info.data))
+ return false;
+
+ int32 const severity = static_cast<int32>(sWorld->getIntConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY));
+ if (severity >= 0)
+ {
+ if (!LinkValidator<TAG>::IsColorValid(t, info.color))
+ return false;
+
+ if (severity >= 1)
+ {
+ if (!LinkValidator<TAG>::IsTextValid(t, info.text))
+ return false;
+ }
+ }
+ return true;
+}
+
+#define TryValidateAs(T) do { if (info.tag == T::tag()) return ValidateAs<T>(info); } while (0);
+
+static bool ValidateLinkInfo(HyperlinkInfo const& info)
+{
+ using namespace LinkTags;
+ TryValidateAs(achievement);
+ TryValidateAs(area);
+ TryValidateAs(areatrigger);
+ TryValidateAs(creature);
+ TryValidateAs(creature_entry);
+ TryValidateAs(enchant);
+ TryValidateAs(gameevent);
+ TryValidateAs(gameobject);
+ TryValidateAs(gameobject_entry);
+ TryValidateAs(glyph);
+ TryValidateAs(item);
+ TryValidateAs(itemset);
+ TryValidateAs(player);
+ TryValidateAs(quest);
+ TryValidateAs(skill);
+ TryValidateAs(spell);
+ TryValidateAs(talent);
+ TryValidateAs(taxinode);
+ TryValidateAs(tele);
+ TryValidateAs(title);
+ TryValidateAs(trade);
+ return false;
+}
+
+// Validates all hyperlinks and control sequences contained in str
+bool Acore::Hyperlinks::CheckAllLinks(std::string_view str)
+{
+ // Step 1: Disallow all control sequences except ||, |H, |h, |c and |r
+ {
+ std::string_view::size_type pos = 0;
+ while ((pos = str.find('|', pos)) != std::string::npos)
+ {
+ ++pos;
+ if (pos == str.length())
+ return false;
+ char next = str[pos];
+ if (next == 'H' || next == 'h' || next == 'c' || next == 'r' || next == '|')
+ ++pos;
+ else
+ return false;
+ }
+ }
+
+ // Step 2: Parse all link sequences
+ // They look like this: |c<color>|H<linktag>:<linkdata>|h[<linktext>]|h|r
+ // - <color> is 8 hex characters AARRGGBB
+ // - <linktag> is arbitrary length [a-z_]
+ // - <linkdata> is arbitrary length, no | contained
+ // - <linktext> is printable
+ {
+ std::string::size_type pos;
+ while ((pos = str.find('|')) != std::string::npos)
+ {
+ if (str[pos + 1] == '|') // this is an escaped pipe character (||)
+ {
+ str = str.substr(pos + 2);
+ continue;
+ }
+
+ HyperlinkInfo info = ParseSingleHyperlink(str.substr(pos));
+ if (!info || !ValidateLinkInfo(info))
+ return false;
+
+ // tag is fine, find the next one
+ str = info.tail;
+ }
+ }
+
+ // all tags are valid
+ return true;
+}
diff --git a/src/server/game/Chat/Hyperlinks.h b/src/server/game/Chat/Hyperlinks.h
new file mode 100644
index 0000000000..07c8f901ca
--- /dev/null
+++ b/src/server/game/Chat/Hyperlinks.h
@@ -0,0 +1,255 @@
+/*
+ * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _HYPERLINKS_H_
+#define _HYPERLINKS_H_
+
+#include "ObjectGuid.h"
+#include "StringConvert.h"
+#include <array>
+#include <string>
+#include <string_view>
+#include <type_traits>
+#include <utility>
+
+struct AchievementEntry;
+struct GlyphPropertiesEntry;
+struct GlyphSlotEntry;
+struct ItemRandomPropertiesEntry;
+struct ItemRandomSuffixEntry;
+struct ItemTemplate;
+class SpellInfo;
+class Quest;
+struct TalentEntry;
+
+namespace Acore::Hyperlinks
+{
+
+ struct AchievementLinkData
+ {
+ AchievementEntry const* Achievement;
+ ObjectGuid CharacterId;
+ bool IsFinished;
+ uint8 Year;
+ uint8 Month;
+ uint8 Day;
+ std::array<uint32, 4> Criteria;
+ };
+
+ struct GlyphLinkData
+ {
+ GlyphPropertiesEntry const* Glyph;
+ GlyphSlotEntry const* Slot;
+ };
+
+ struct ItemLinkData
+ {
+ ItemTemplate const* Item;
+ uint32 EnchantId;
+ std::array<uint32, 3> GemEnchantId;
+ ItemRandomPropertiesEntry const* RandomProperty;
+ ItemRandomSuffixEntry const* RandomSuffix;
+ uint32 RandomSuffixBaseAmount; /* ITEM_FIELD_PROPERTY_SEED - only nonzero for RandomSuffix items, AllocationPct from DBC are multiplied with this, then floored, to get stat value */
+ uint8 RenderLevel;
+ bool IsBuggedInspectLink;
+ };
+
+ struct QuestLinkData
+ {
+ ::Quest const* Quest;
+ int16 QuestLevel;
+ };
+
+ struct TalentLinkData
+ {
+ TalentEntry const* Talent;
+ uint8 Rank;
+ SpellInfo const* Spell;
+ };
+
+ struct TradeskillLinkData
+ {
+ SpellInfo const* Spell;
+ uint16 CurValue;
+ uint16 MaxValue;
+ ObjectGuid Owner;
+ std::string KnownRecipes;
+ };
+
+ namespace LinkTags {
+
+ /************************** LINK TAGS ***************************************************\
+ |* Link tags must abide by the following: *|
+ |* - MUST expose ::value_type typedef *|
+ |* - storage type is remove_cvref_t<value_type> *|
+ |* - MUST expose static ::tag method, void -> std::string_view *|
+ |* - this method SHOULD be constexpr *|
+ |* - returns identifier string for the link ("creature", "creature_entry", "item") *|
+ |* - MUST expose static ::StoreTo method, (storage&, std::string_view) *|
+ |* - assign storage& based on content of std::string_view *|
+ |* - return value indicates success/failure *|
+ |* - for integral/string types this can be achieved by extending base_tag *|
+ \****************************************************************************************/
+ struct base_tag
+ {
+ static bool StoreTo(std::string_view& val, std::string_view data)
+ {
+ val = data;
+ return true;
+ }
+
+ static bool StoreTo(std::string& val, std::string_view data)
+ {
+ val.assign(data);
+ return true;
+ }
+
+ template <typename T>
+ static std::enable_if_t<std::is_integral_v<T>, bool> StoreTo(T& val, std::string_view data)
+ {
+ if (Optional<T> res = Acore::StringTo<T>(data))
+ {
+ val = *res;
+ return true;
+ }
+ else
+ return false;
+ }
+
+ static bool StoreTo(ObjectGuid& val, std::string_view data)
+ {
+ if (Optional<uint64> res = Acore::StringTo<uint64>(data, 16))
+ {
+ val.Set(*res);
+ return true;
+ }
+ else
+ return false;
+ }
+ };
+
+ #define make_base_tag(ltag, type) struct ltag : public base_tag { using value_type = type; static constexpr std::string_view tag() { return #ltag; } }
+ make_base_tag(area, uint32);
+ make_base_tag(areatrigger, uint32);
+ make_base_tag(creature, ObjectGuid::LowType);
+ make_base_tag(creature_entry, uint32);
+ make_base_tag(gameevent, uint16);
+ make_base_tag(gameobject, ObjectGuid::LowType);
+ make_base_tag(gameobject_entry, uint32);
+ make_base_tag(itemset, uint32);
+ make_base_tag(player, std::string_view);
+ make_base_tag(skill, uint32);
+ make_base_tag(taxinode, uint32);
+ make_base_tag(tele, uint32);
+ make_base_tag(title, uint32);
+ #undef make_base_tag
+
+ struct AC_GAME_API achievement
+ {
+ using value_type = AchievementLinkData const&;
+ static constexpr std::string_view tag() { return "achievement"; }
+ static bool StoreTo(AchievementLinkData& val, std::string_view data);
+ };
+
+ struct AC_GAME_API enchant
+ {
+ using value_type = SpellInfo const*;
+ static constexpr std::string_view tag() { return "enchant"; }
+ static bool StoreTo(SpellInfo const*& val, std::string_view data);
+ };
+
+ struct AC_GAME_API glyph
+ {
+ using value_type = GlyphLinkData const&;
+ static constexpr std::string_view tag() { return "glyph"; };
+ static bool StoreTo(GlyphLinkData& val, std::string_view data);
+ };
+
+ struct AC_GAME_API item
+ {
+ using value_type = ItemLinkData const&;
+ static constexpr std::string_view tag() { return "item"; }
+ static bool StoreTo(ItemLinkData& val, std::string_view data);
+ };
+
+ struct AC_GAME_API quest
+ {
+ using value_type = QuestLinkData const&;
+ static constexpr std::string_view tag() { return "quest"; }
+ static bool StoreTo(QuestLinkData& val, std::string_view data);
+ };
+
+ struct AC_GAME_API spell
+ {
+ using value_type = SpellInfo const*;
+ static constexpr std::string_view tag() { return "spell"; }
+ static bool StoreTo(SpellInfo const*& val, std::string_view data);
+ };
+
+ struct AC_GAME_API talent
+ {
+ using value_type = TalentLinkData const&;
+ static constexpr std::string_view tag() { return "talent"; }
+ static bool StoreTo(TalentLinkData& val, std::string_view data);
+ };
+
+ struct AC_GAME_API trade
+ {
+ using value_type = TradeskillLinkData const&;
+ static constexpr std::string_view tag() { return "trade"; }
+ static bool StoreTo(TradeskillLinkData& val, std::string_view data);
+ };
+ }
+
+ struct HyperlinkColor
+ {
+ HyperlinkColor(uint32 c) : r(c >> 16), g(c >> 8), b(c), a(c >> 24) {}
+ uint8 const r, g, b, a;
+ bool operator==(uint32 c) const
+ {
+ if ((c & 0xff) ^ b)
+ return false;
+ if (((c >>= 8) & 0xff) ^ g)
+ return false;
+ if (((c >>= 8) & 0xff) ^ r)
+ return false;
+ if ((c >>= 8) ^ a)
+ return false;
+ return true;
+ }
+ };
+
+ struct HyperlinkInfo
+ {
+ HyperlinkInfo() : ok(false), color(0) {}
+ HyperlinkInfo(std::string_view t, uint32 c, std::string_view ta, std::string_view d, std::string_view te) :
+ ok(true), tail(t), color(c), tag(ta), data(d), text(te) {}
+
+ explicit operator bool() { return ok; }
+ bool const ok;
+ std::string_view const tail;
+ HyperlinkColor const color;
+ std::string_view const tag;
+ std::string_view const data;
+ std::string_view const text;
+ };
+ HyperlinkInfo AC_GAME_API ParseSingleHyperlink(std::string_view str);
+ bool AC_GAME_API CheckAllLinks(std::string_view str);
+
+}
+
+#endif
diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp
index 9a42b884c1..fd4af3f559 100644
--- a/src/server/game/DataStores/DBCStores.cpp
+++ b/src/server/game/DataStores/DBCStores.cpp
@@ -903,7 +903,7 @@ SimpleFactionsList const* GetFactionTeamList(uint32 faction)
return nullptr;
}
-char* GetPetName(uint32 petfamily, uint32 dbclang)
+char const* GetPetName(uint32 petfamily, uint32 dbclang)
{
if (!petfamily)
return nullptr;
@@ -912,7 +912,7 @@ char* GetPetName(uint32 petfamily, uint32 dbclang)
if (!pet_family)
return nullptr;
- return pet_family->Name[dbclang] ? pet_family->Name[dbclang] : nullptr;
+ return pet_family->Name[dbclang];
}
TalentSpellPos const* GetTalentSpellPos(uint32 spellId)
diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h
index f5dc07d736..2ff299c991 100644
--- a/src/server/game/DataStores/DBCStores.h
+++ b/src/server/game/DataStores/DBCStores.h
@@ -29,7 +29,7 @@ typedef std::vector<FlyByCamera> FlyByCameraCollection;
SimpleFactionsList const* GetFactionTeamList(uint32 faction);
-char* GetPetName(uint32 petfamily, uint32 dbclang);
+char const* GetPetName(uint32 petfamily, uint32 dbclang);
uint32 GetTalentSpellCost(uint32 spellId);
TalentSpellPos const* GetTalentSpellPos(uint32 spellId);
diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp
index 2191709574..cb27a9b2e2 100644
--- a/src/server/game/Entities/Item/Item.cpp
+++ b/src/server/game/Entities/Item/Item.cpp
@@ -644,7 +644,7 @@ void Item::SetItemRandomProperties(int32 randomPropId)
SetState(ITEM_CHANGED, GetOwner());
}
for (uint32 i = PROP_ENCHANTMENT_SLOT_0; i < MAX_ENCHANTMENT_SLOT; ++i)
- SetEnchantment(EnchantmentSlot(i), item_rand->enchant_id[i - PROP_ENCHANTMENT_SLOT_0], 0, 0);
+ SetEnchantment(EnchantmentSlot(i), item_rand->Enchantment[i - PROP_ENCHANTMENT_SLOT_0], 0, 0);
}
}
else
@@ -661,7 +661,7 @@ void Item::SetItemRandomProperties(int32 randomPropId)
}
for (uint32 i = PROP_ENCHANTMENT_SLOT_0; i < MAX_ENCHANTMENT_SLOT; ++i)
- SetEnchantment(EnchantmentSlot(i), item_rand->enchant_id[i - PROP_ENCHANTMENT_SLOT_0], 0, 0);
+ SetEnchantment(EnchantmentSlot(i), item_rand->Enchantment[i - PROP_ENCHANTMENT_SLOT_0], 0, 0);
}
}
}
diff --git a/src/server/game/Entities/Player/PlayerStorage.cpp b/src/server/game/Entities/Player/PlayerStorage.cpp
index b2aa832898..28722b45fb 100644
--- a/src/server/game/Entities/Player/PlayerStorage.cpp
+++ b/src/server/game/Entities/Player/PlayerStorage.cpp
@@ -4420,9 +4420,9 @@ void Player::ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool
// Search enchant_amount
for (int k = 0; k < MAX_ITEM_ENCHANTMENT_EFFECTS; ++k)
{
- if (item_rand->enchant_id[k] == enchant_id)
+ if (item_rand->Enchantment[k] == enchant_id)
{
- basepoints = int32((item_rand->prefix[k] * item->GetItemSuffixFactor()) / 10000);
+ basepoints = int32((item_rand->AllocationPct[k] * item->GetItemSuffixFactor()) / 10000);
break;
}
}
@@ -4446,9 +4446,9 @@ void Player::ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool
{
for (int k = 0; k < MAX_ITEM_ENCHANTMENT_EFFECTS; ++k)
{
- if (item_rand->enchant_id[k] == enchant_id)
+ if (item_rand->Enchantment[k] == enchant_id)
{
- enchant_amount = uint32((item_rand->prefix[k] * item->GetItemSuffixFactor()) / 10000);
+ enchant_amount = uint32((item_rand->AllocationPct[k] * item->GetItemSuffixFactor()) / 10000);
break;
}
}
@@ -4466,9 +4466,9 @@ void Player::ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool
{
for (int k = 0; k < MAX_ITEM_ENCHANTMENT_EFFECTS; ++k)
{
- if (item_rand_suffix->enchant_id[k] == enchant_id)
+ if (item_rand_suffix->Enchantment[k] == enchant_id)
{
- enchant_amount = uint32((item_rand_suffix->prefix[k] * item->GetItemSuffixFactor()) / 10000);
+ enchant_amount = uint32((item_rand_suffix->AllocationPct[k] * item->GetItemSuffixFactor()) / 10000);
break;
}
}
@@ -6721,7 +6721,7 @@ void Player::PrettyPrintRequirementsAchievementsList(const std::vector<const Pro
continue;
}
- std::string name = *achievementEntry->name;
+ std::string name = achievementEntry->name[sObjectMgr->GetDBCLocaleIndex()];
std::stringstream stream;
stream << "|cffff7c0a|Hachievement:";
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index 5c0ee91ea1..c7d4a8a06e 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -6981,7 +6981,7 @@ std::string ObjectMgr::GeneratePetName(uint32 entry)
if (list0.empty() || list1.empty())
{
CreatureTemplate const* cinfo = GetCreatureTemplate(entry);
- char* petname = GetPetName(cinfo->family, sWorld->GetDefaultDbcLocale());
+ char const* petname = GetPetName(cinfo->family, sWorld->GetDefaultDbcLocale());
if (!petname)
return cinfo->Name;
@@ -8124,7 +8124,7 @@ void ObjectMgr::LoadGameTele()
LOG_INFO("server.loading", " ");
}
-GameTele const* ObjectMgr::GetGameTele(const std::string& name) const
+GameTele const* ObjectMgr::GetGameTele(std::string_view name) const
{
// explicit name case
std::wstring wname;
@@ -8180,7 +8180,7 @@ bool ObjectMgr::AddGameTele(GameTele& tele)
return true;
}
-bool ObjectMgr::DeleteGameTele(const std::string& name)
+bool ObjectMgr::DeleteGameTele(std::string_view name)
{
// explicit name case
std::wstring wname;
diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h
index a807d2c84c..4fb01e6d0e 100644
--- a/src/server/game/Globals/ObjectMgr.h
+++ b/src/server/game/Globals/ObjectMgr.h
@@ -1287,10 +1287,10 @@ public:
if (itr == _gameTeleStore.end()) return nullptr;
return &itr->second;
}
- [[nodiscard]] GameTele const* GetGameTele(std::string const& name) const;
+ [[nodiscard]] GameTele const* GetGameTele(std::string_view name) const;
[[nodiscard]] GameTeleContainer const& GetGameTeleMap() const { return _gameTeleStore; }
bool AddGameTele(GameTele& data);
- bool DeleteGameTele(std::string const& name);
+ bool DeleteGameTele(std::string_view name);
[[nodiscard]] TrainerSpellData const* GetNpcTrainerSpells(uint32 entry) const
{
@@ -1344,6 +1344,13 @@ public:
}
static void AddLocaleString(std::string&& s, LocaleConstant locale, std::vector<std::string>& data);
+ static std::string_view GetLocaleString(std::vector<std::string> const& data, size_t locale)
+ {
+ if (locale < data.size())
+ return data[locale];
+ else
+ return {};
+ }
static inline void GetLocaleString(const std::vector<std::string>& data, int loc_idx, std::string& value)
{
if (data.size() > size_t(loc_idx) && !data[loc_idx].empty())
diff --git a/src/server/game/Handlers/ChannelHandler.cpp b/src/server/game/Handlers/ChannelHandler.cpp
index bb6e1c0668..4e16b5fde4 100644
--- a/src/server/game/Handlers/ChannelHandler.cpp
+++ b/src/server/game/Handlers/ChannelHandler.cpp
@@ -46,10 +46,10 @@ void WorldSession::HandleJoinChannel(WorldPacket& recvPacket)
if (isdigit(channelName[0]))
return;
- // pussywizard: restrict allowed characters in channel name to avoid |0 and possibly other exploits
- //if (!ObjectMgr::IsValidChannelName(channelName))
- if (channelName.find("|") != std::string::npos || channelName.size() >= 100)
+ if (channelName.size() >= 100 || !DisallowHyperlinksAndMaybeKick(channelName))
+ {
return;
+ }
if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeamId()))
{
diff --git a/src/server/game/Handlers/ChatHandler.cpp b/src/server/game/Handlers/ChatHandler.cpp
index d301b81d99..df7e5c6879 100644
--- a/src/server/game/Handlers/ChatHandler.cpp
+++ b/src/server/game/Handlers/ChatHandler.cpp
@@ -304,20 +304,6 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData)
SendNotification(GetAcoreString(LANG_WAIT_BEFORE_SPEAKING), timeStr.c_str());
return;
}
-
- if (lang != LANG_ADDON)
- {
- if (sWorld->getIntConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY) && !ChatHandler(this).isValidChatMessage(msg.c_str()))
- {
- //LOG_ERROR("network.opcode", "Player %s (%s) sent a chatmessage with an invalid link: %s", GetPlayer()->GetName().c_str(),
- // GetPlayer()->GetGUID().ToString().c_str(), msg.c_str());
-
- if (sWorld->getIntConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_KICK))
- KickPlayer("CONFIG_CHAT_STRICT_LINK_CHECKING_KICK");
-
- return;
- }
- }
}
// do message validity checks
@@ -352,29 +338,14 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData)
auto end = std::unique(msg.begin(), msg.end(), [](char c1, char c2) { return (c1 == ' ') && (c2 == ' '); });
msg.erase(end, msg.end());
}
- }
- // exploit
- size_t found1 = msg.find("|Hquest");
- if (found1 != std::string::npos)
- {
- size_t found2 = msg.find(":", found1 + 8);
- size_t found3 = msg.find("|", found1 + 8);
- if (found3 != std::string::npos)
+ // Validate hyperlinks
+ if (!ValidateHyperlinksAndMaybeKick(msg))
{
- if (found2 == std::string::npos)
- return;
- if (found2 > found3)
- return;
+ return;
}
}
- // prevent crash player
- if (msg.find("| |Hquest") != std::string::npos)
- {
- return;
- }
-
sScriptMgr->OnBeforeSendChatMessage(_player, type, lang, msg);
switch (type)
diff --git a/src/server/game/Handlers/TicketHandler.cpp b/src/server/game/Handlers/TicketHandler.cpp
index 17ebabb333..b7485443a8 100644
--- a/src/server/game/Handlers/TicketHandler.cpp
+++ b/src/server/game/Handlers/TicketHandler.cpp
@@ -61,6 +61,11 @@ void WorldSession::HandleGMTicketCreateOpcode(WorldPacket& recvData)
recvData >> x >> y >> z;
recvData >> message;
+ if (!ValidateHyperlinksAndMaybeKick(message))
+ {
+ return;
+ }
+
recvData >> needResponse;
recvData >> needMoreHelp;
@@ -96,6 +101,11 @@ void WorldSession::HandleGMTicketCreateOpcode(WorldPacket& recvData)
recvData.rfinish(); // Will still have compressed data in buffer.
}
+ if (!chatLog.empty() && !ValidateHyperlinksAndMaybeKick(chatLog))
+ {
+ return;
+ }
+
ticket = new GmTicket(GetPlayer());
ticket->SetPosition(mapId, x, y, z);
ticket->SetMessage(message);
@@ -122,6 +132,11 @@ void WorldSession::HandleGMTicketUpdateOpcode(WorldPacket& recv_data)
std::string message;
recv_data >> message;
+ if (!ValidateHyperlinksAndMaybeKick(message))
+ {
+ return;
+ }
+
GMTicketResponse response = GMTICKET_RESPONSE_UPDATE_ERROR;
if (GmTicket* ticket = sTicketMgr->GetTicketByPlayer(GetPlayer()->GetGUID()))
{
@@ -187,6 +202,7 @@ void WorldSession::HandleGMSurveySubmit(WorldPacket& recv_data)
std::unordered_set<uint32> surveyIds;
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
+
// sub_survey1, r1, comment1, sub_survey2, r2, comment2, sub_survey3, r3, comment3, sub_survey4, r4, comment4, sub_survey5, r5, comment5, sub_survey6, r6, comment6, sub_survey7, r7, comment7, sub_survey8, r8, comment8, sub_survey9, r9, comment9, sub_survey10, r10, comment10,
for (uint8 i = 0; i < 10; i++)
{
@@ -200,6 +216,11 @@ void WorldSession::HandleGMSurveySubmit(WorldPacket& recv_data)
std::string comment; // comment ("Usage: GMSurveyAnswerSubmit(question, rank, comment)")
recv_data >> comment;
+ if (!ValidateHyperlinksAndMaybeKick(comment))
+ {
+ return;
+ }
+
// make sure the same sub survey is not added to DB twice
if (!surveyIds.insert(subSurveyId).second)
continue;
@@ -215,6 +236,11 @@ void WorldSession::HandleGMSurveySubmit(WorldPacket& recv_data)
std::string comment; // just a guess
recv_data >> comment;
+ if (!ValidateHyperlinksAndMaybeKick(comment))
+ {
+ return;
+ }
+
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GM_SURVEY);
stmt->setUInt32(0, GetPlayer()->GetGUID().GetCounter());
stmt->setUInt32(1, nextSurveyID);
diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h
index a344bac872..17e7b115bb 100644
--- a/src/server/game/Miscellaneous/Language.h
+++ b/src/server/game/Miscellaneous/Language.h
@@ -35,10 +35,10 @@ enum AcoreStrings
LANG_SYSTEMMESSAGE = 3,
LANG_EVENTMESSAGE = 4,
LANG_NO_HELP_CMD = 5,
- LANG_NO_CMD = 6,
- LANG_NO_SUBCMD = 7,
+ LANG_CMD_INVALID = 6,
+ LANG_SUBCMD_AMBIGUOUS = 7,
LANG_SUBCMDS_LIST = 8,
- LANG_AVIABLE_CMD = 9,
+ LANG_AVAILABLE_CMDS = 9,
LANG_CMD_SYNTAX = 10,
LANG_ACCOUNT_LEVEL = 11,
LANG_CONNECTED_USERS = 12,
@@ -222,7 +222,13 @@ enum AcoreStrings
LANG_2FA_SECRET_TOO_LONG = 188,
LANG_2FA_SECRET_INVALID = 189,
LANG_2FA_SECRET_SET_COMPLETE = 190,
- // free 191 - 199
+ LANG_SUBCMDS_LIST_ENTRY = 191,
+ LANG_SUBCMDS_LIST_ENTRY_ELLIPSIS = 192,
+ LANG_SUBCMD_INVALID = 193,
+ LANG_CMD_AMBIGUOUS = 194,
+ LANG_CMD_HELP_GENERIC = 195,
+ LANG_CMD_NO_HELP_AVAILABLE = 196,
+ // Room for more level 1 197-199 not used
// level 2 chat
LANG_NO_SELECTION = 200,
@@ -1089,7 +1095,26 @@ enum AcoreStrings
//Player Ticket Strings
LANG_TICKET_CLOSED = 1334,
LANG_TICKET_COMPLETED = 1335,
- // FREE IDS 1336-1999
+ // FREE IDS 1336-1499
+
+ // Command argument parsers
+ LANG_CMDPARSER_EITHER = 1500,
+ LANG_CMDPARSER_OR = 1501,
+ LANG_CMDPARSER_STRING_VALUE_INVALID = 1502,
+ LANG_CMDPARSER_INVALID_UTF8 = 1503,
+ LANG_CMDPARSER_LINKDATA_INVALID = 1504,
+ LANG_CMDPARSER_ACCOUNT_NAME_NO_EXIST = 1505,
+ LANG_CMDPARSER_ACCOUNT_ID_NO_EXIST = 1506,
+ LANG_CMDPARSER_CHAR_GUID_NO_EXIST = 1507,
+ LANG_CMDPARSER_CHAR_NAME_NO_EXIST = 1508,
+ LANG_CMDPARSER_CHAR_NAME_INVALID = 1509,
+ LANG_CMDPARSER_ACHIEVEMENT_NO_EXIST = 1510,
+ LANG_CMDPARSER_GAME_TELE_ID_NO_EXIST = 1511,
+ LANG_CMDPARSER_GAME_TELE_NO_EXIST = 1512,
+ LANG_CMDPARSER_ITEM_NO_EXIST = 1513,
+ LANG_CMDPARSER_SPELL_NO_EXIST = 1514,
+ LANG_CMDPARSER_EXACT_SEQ_MISMATCH = 1515,
+ // FREE IDS 1516-1499
// Ticket Strings 2000-2030
LANG_COMMAND_TICKETNEW = 2000,
diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp
index 113229b1c2..e74b2b51e9 100644
--- a/src/server/game/Scripting/ScriptMgr.cpp
+++ b/src/server/game/Scripting/ScriptMgr.cpp
@@ -1056,22 +1056,16 @@ OutdoorPvP* ScriptMgr::CreateOutdoorPvP(OutdoorPvPData const* data)
return tmpscript->GetOutdoorPvP();
}
-std::vector<ChatCommand> ScriptMgr::GetChatCommands()
+Acore::ChatCommands::ChatCommandTable ScriptMgr::GetChatCommands()
{
- std::vector<ChatCommand> table;
+ Acore::ChatCommands::ChatCommandTable table;
- FOR_SCRIPTS_RET(CommandScript, itr, end, table)
+ FOR_SCRIPTS(CommandScript, itr, end)
{
- std::vector<ChatCommand> cmds = itr->second->GetCommands();
- table.insert(table.end(), cmds.begin(), cmds.end());
+ Acore::ChatCommands::ChatCommandTable cmds = itr->second->GetCommands();
+ std::move(cmds.begin(), cmds.end(), std::back_inserter(table));
}
- // Sort commands in alphabetical order
- std::sort(table.begin(), table.end(), [](const ChatCommand & a, const ChatCommand & b)
- {
- return strcmp(a.Name, b.Name) < 0;
- });
-
return table;
}
diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h
index 532ff0a75c..0b5cae72b6 100644
--- a/src/server/game/Scripting/ScriptMgr.h
+++ b/src/server/game/Scripting/ScriptMgr.h
@@ -41,7 +41,6 @@ class Battleground;
class BattlegroundMap;
class BattlegroundQueue;
class Channel;
-class ChatCommand;
class Creature;
class CreatureAI;
class DynamicObject;
@@ -81,6 +80,11 @@ struct OutdoorPvPData;
struct GroupQueueInfo;
struct TargetInfo;
+namespace Acore::ChatCommands
+{
+ struct ChatCommandBuilder;
+}
+
#define VISIBLE_RANGE 166.0f //MAX visible range (size of grid)
// Check out our guide on how to create new hooks in our wiki! https://www.azerothcore.org/wiki/hooks-script
@@ -579,7 +583,7 @@ protected:
public:
// Should return a pointer to a valid command table (ChatCommand array) to be used by ChatHandler.
- [[nodiscard]] virtual std::vector<ChatCommand> GetCommands() const = 0;
+ [[nodiscard]] virtual std::vector<Acore::ChatCommands::ChatCommandBuilder> GetCommands() const = 0;
};
class WeatherScript : public ScriptObject, public UpdatableScript<Weather>
@@ -1566,7 +1570,7 @@ public: /* OutdoorPvPScript */
OutdoorPvP* CreateOutdoorPvP(OutdoorPvPData const* data);
public: /* CommandScript */
- std::vector<ChatCommand> GetChatCommands();
+ std::vector<Acore::ChatCommands::ChatCommandBuilder> GetChatCommands();
public: /* WeatherScript */
void OnWeatherChange(Weather* weather, WeatherState state, float grade);
diff --git a/src/server/game/Server/Packets/PacketUtilities.cpp b/src/server/game/Server/Packets/PacketUtilities.cpp
index 91d1d5f27f..ba1753e47a 100644
--- a/src/server/game/Server/Packets/PacketUtilities.cpp
+++ b/src/server/game/Server/Packets/PacketUtilities.cpp
@@ -16,7 +16,7 @@
*/
#include "PacketUtilities.h"
-//#include "Hyperlinks.h"
+#include "Hyperlinks.h"
#include "Errors.h"
#include <utf8.h>
#include <sstream>
@@ -41,13 +41,13 @@ bool WorldPackets::Strings::Utf8::Validate(std::string const& value)
return true;
}
-//bool WorldPackets::Strings::Hyperlinks::Validate(std::string const& value)
-//{
-// if (!Acore::Hyperlinks::CheckAllLinks(value))
-// throw InvalidHyperlinkException(value);
-//
-// return true;
-//}
+bool WorldPackets::Strings::Hyperlinks::Validate(std::string const& value)
+{
+ if (!Acore::Hyperlinks::CheckAllLinks(value))
+ throw InvalidHyperlinkException(value);
+
+ return true;
+}
bool WorldPackets::Strings::NoHyperlinks::Validate(std::string const& value)
{
diff --git a/src/server/game/Server/Packets/PacketUtilities.h b/src/server/game/Server/Packets/PacketUtilities.h
index bc44247f18..bb00547f5b 100644
--- a/src/server/game/Server/Packets/PacketUtilities.h
+++ b/src/server/game/Server/Packets/PacketUtilities.h
@@ -59,7 +59,7 @@ namespace WorldPackets
template<std::size_t MaxBytesWithoutNullTerminator>
struct ByteSize { static bool Validate(std::string const& value) { return value.size() <= MaxBytesWithoutNullTerminator; } };
struct Utf8 { static bool Validate(std::string const& value); };
- //struct Hyperlinks { static bool Validate(std::string const& value); };
+ struct Hyperlinks { static bool Validate(std::string const& value); };
struct NoHyperlinks { static bool Validate(std::string const& value); };
}
diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp
index cf304e15e7..0cc33d3308 100644
--- a/src/server/game/Server/WorldSession.cpp
+++ b/src/server/game/Server/WorldSession.cpp
@@ -27,6 +27,7 @@
#include "Group.h"
#include "Guild.h"
#include "GuildMgr.h"
+#include "Hyperlinks.h"
#include "Log.h"
#include "MapMgr.h"
#include "ObjectAccessor.h"
@@ -389,6 +390,26 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
break;
}
}
+ catch (WorldPackets::InvalidHyperlinkException const& ihe)
+ {
+ LOG_ERROR("network", "%s sent %s with an invalid link:\n%s", GetPlayerInfo().c_str(),
+ GetOpcodeNameForLogging(static_cast<OpcodeClient>(packet->GetOpcode())).c_str(), ihe.GetInvalidValue().c_str());
+
+ if (sWorld->getIntConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_KICK))
+ {
+ KickPlayer("WorldSession::Update Invalid chat link");
+ }
+ }
+ catch (WorldPackets::IllegalHyperlinkException const& ihe)
+ {
+ LOG_ERROR("network", "%s sent %s which illegally contained a hyperlink:\n%s", GetPlayerInfo().c_str(),
+ GetOpcodeNameForLogging(static_cast<OpcodeClient>(packet->GetOpcode())).c_str(), ihe.GetInvalidValue().c_str());
+
+ if (sWorld->getIntConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_KICK))
+ {
+ KickPlayer("WorldSession::Update Illegal chat link");
+ }
+ }
catch (WorldPackets::PacketArrayMaxCapacityException const& pamce)
{
LOG_ERROR("network", "PacketArrayMaxCapacityException: %s while parsing %s from %s.",
@@ -698,6 +719,34 @@ void WorldSession::KickPlayer(std::string const& reason, bool setKicked)
SetKicked(true); // pussywizard: the session won't be left ingame for 60 seconds and to also kick offline session
}
+bool WorldSession::ValidateHyperlinksAndMaybeKick(std::string_view str)
+{
+ if (Acore::Hyperlinks::CheckAllLinks(str))
+ return true;
+
+ LOG_ERROR("network", "Player %s%s sent a message with an invalid link:\n%.*s", GetPlayer()->GetName().c_str(),
+ GetPlayer()->GetGUID().ToString().c_str(), STRING_VIEW_FMT_ARG(str));
+
+ if (sWorld->getIntConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_KICK))
+ KickPlayer("WorldSession::ValidateHyperlinksAndMaybeKick Invalid chat link");
+
+ return false;
+}
+
+bool WorldSession::DisallowHyperlinksAndMaybeKick(std::string_view str)
+{
+ if (str.find('|') == std::string_view::npos)
+ return true;
+
+ LOG_ERROR("network", "Player %s %s sent a message which illegally contained a hyperlink:\n%.*s", GetPlayer()->GetName().c_str(),
+ GetPlayer()->GetGUID().ToString().c_str(), STRING_VIEW_FMT_ARG(str));
+
+ if (sWorld->getIntConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_KICK))
+ KickPlayer("WorldSession::DisallowHyperlinksAndMaybeKick Illegal chat link");
+
+ return false;
+}
+
void WorldSession::SendNotification(const char* format, ...)
{
if (format)
diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h
index b28ae8b6b2..c955b5eb1d 100644
--- a/src/server/game/Server/WorldSession.h
+++ b/src/server/game/Server/WorldSession.h
@@ -309,6 +309,13 @@ public:
void KickPlayer(bool setKicked = true) { return this->KickPlayer("Unknown reason", setKicked); }
void KickPlayer(std::string const& reason, bool setKicked = true);
+ // Returns true if all contained hyperlinks are valid
+ // May kick player on false depending on world config (handler should abort)
+ bool ValidateHyperlinksAndMaybeKick(std::string_view str);
+ // Returns true if the message contains no hyperlinks
+ // May kick player on false depending on world config (handler should abort)
+ bool DisallowHyperlinksAndMaybeKick(std::string_view str);
+
void QueuePacket(WorldPacket* new_packet);
bool Update(uint32 diff, PacketFilter& updater);
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
index 992d2fb66c..6c169bc4dc 100644
--- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp
+++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
@@ -466,13 +466,13 @@ int32 AuraEffect::CalculateAmount(Unit* caster)
{
for (uint8 k = 0; k < MAX_ITEM_ENCHANTMENT_EFFECTS; k++)
{
- SpellItemEnchantmentEntry const* pEnchant = sSpellItemEnchantmentStore.LookupEntry(item_rand_suffix->enchant_id[k]);
+ SpellItemEnchantmentEntry const* pEnchant = sSpellItemEnchantmentStore.LookupEntry(item_rand_suffix->Enchantment[k]);
if (pEnchant)
{
for (uint8 t = 0; t < MAX_SPELL_ITEM_ENCHANTMENT_EFFECTS; t++)
if (pEnchant->spellid[t] == m_spellInfo->Id)
{
- amount = uint32((item_rand_suffix->prefix[k] * castItem->GetItemSuffixFactor()) / 10000);
+ amount = uint32((item_rand_suffix->AllocationPct[k] * castItem->GetItemSuffixFactor()) / 10000);
break;
}
}
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index 67780c75fa..086c52c3c3 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -936,13 +936,18 @@ void Spell::SelectEffectImplicitTargets(SpellEffIndex effIndex, SpellImplicitTar
case TARGET_SELECT_CATEGORY_NEARBY:
case TARGET_SELECT_CATEGORY_CONE:
case TARGET_SELECT_CATEGORY_AREA:
+ {
// targets for effect already selected
if (effectMask & processedEffectMask)
+ {
return;
+ }
+
+ auto const& effects = GetSpellInfo()->Effects;
+
// choose which targets we can select at once
for (uint32 j = effIndex + 1; j < MAX_SPELL_EFFECTS; ++j)
{
- SpellEffectInfo const* effects = GetSpellInfo()->Effects;
if (effects[j].IsEffect() &&
effects[effIndex].TargetA.GetTarget() == effects[j].TargetA.GetTarget() &&
effects[effIndex].TargetB.GetTarget() == effects[j].TargetB.GetTarget() &&
@@ -955,6 +960,7 @@ void Spell::SelectEffectImplicitTargets(SpellEffIndex effIndex, SpellImplicitTar
}
processedEffectMask |= effectMask;
break;
+ }
default:
break;
}
diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp
index 7cd2c5a029..ea68396fd7 100644
--- a/src/server/game/Spells/SpellInfo.cpp
+++ b/src/server/game/Spells/SpellInfo.cpp
@@ -207,8 +207,8 @@ uint32 SpellImplicitTargetInfo::GetExplicitTargetMask(bool& srcSet, bool& dstSet
return targetMask;
}
-SpellImplicitTargetInfo::StaticData SpellImplicitTargetInfo::_data[TOTAL_SPELL_TARGETS] =
-{
+std::array<SpellImplicitTargetInfo::StaticData, TOTAL_SPELL_TARGETS> SpellImplicitTargetInfo::_data =
+{ {
{TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, //
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 1 TARGET_UNIT_CASTER
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_NEARBY, TARGET_CHECK_ENEMY, TARGET_DIR_NONE}, // 2 TARGET_UNIT_NEARBY_ENEMY
@@ -320,7 +320,7 @@ SpellImplicitTargetInfo::StaticData SpellImplicitTargetInfo::_data[TOTAL_SPELL_
{TARGET_OBJECT_TYPE_GOBJ, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_CONE, TARGET_CHECK_DEFAULT, TARGET_DIR_FRONT}, // 108 TARGET_GAMEOBJECT_CONE
{TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 109
{TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 110 TARGET_DEST_UNK_110
-};
+} };
SpellEffectInfo::SpellEffectInfo(SpellEntry const* spellEntry, SpellInfo const* spellInfo, uint8 effIndex)
{
@@ -598,8 +598,8 @@ SpellTargetObjectTypes SpellEffectInfo::GetUsedTargetObjectType() const
return _data[Effect].UsedTargetObjectType;
}
-SpellEffectInfo::StaticData SpellEffectInfo::_data[TOTAL_SPELL_EFFECTS] =
-{
+std::array<SpellEffectInfo::StaticData, TOTAL_SPELL_EFFECTS> SpellEffectInfo::_data =
+{ {
// implicit target type used target object type
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 0
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 1 SPELL_EFFECT_INSTAKILL
@@ -766,7 +766,7 @@ SpellEffectInfo::StaticData SpellEffectInfo::_data[TOTAL_SPELL_EFFECTS] =
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 162 SPELL_EFFECT_TALENT_SPEC_SELECT
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 163 SPELL_EFFECT_163
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 164 SPELL_EFFECT_REMOVE_AURA
-};
+} };
SpellInfo::SpellInfo(SpellEntry const* spellEntry)
{
@@ -822,25 +822,18 @@ SpellInfo::SpellInfo(SpellEntry const* spellEntry)
RangeEntry = spellEntry->RangeIndex ? sSpellRangeStore.LookupEntry(spellEntry->RangeIndex) : nullptr;
Speed = spellEntry->Speed;
StackAmount = spellEntry->StackAmount;
- for (uint8 i = 0; i < 2; ++i)
- Totem[i] = spellEntry->Totem[i];
- for (uint8 i = 0; i < MAX_SPELL_REAGENTS; ++i)
- Reagent[i] = spellEntry->Reagent[i];
- for (uint8 i = 0; i < MAX_SPELL_REAGENTS; ++i)
- ReagentCount[i] = spellEntry->ReagentCount[i];
+ Totem = spellEntry->Totem;
+ Reagent = spellEntry->Reagent;
+ ReagentCount = spellEntry->ReagentCount;
EquippedItemClass = spellEntry->EquippedItemClass;
EquippedItemSubClassMask = spellEntry->EquippedItemSubClassMask;
EquippedItemInventoryTypeMask = spellEntry->EquippedItemInventoryTypeMask;
- for (uint8 i = 0; i < 2; ++i)
- TotemCategory[i] = spellEntry->TotemCategory[i];
- for (uint8 i = 0; i < 2; ++i)
- SpellVisual[i] = spellEntry->SpellVisual[i];
+ TotemCategory = spellEntry->TotemCategory;
+ SpellVisual = spellEntry->SpellVisual;
SpellIconID = spellEntry->SpellIconID;
ActiveIconID = spellEntry->ActiveIconID;
- for (uint8 i = 0; i < 16; ++i)
- SpellName[i] = spellEntry->SpellName[i];
- for (uint8 i = 0; i < 16; ++i)
- Rank[i] = spellEntry->Rank[i];
+ SpellName = spellEntry->SpellName;
+ Rank = spellEntry->Rank;
MaxTargetLevel = spellEntry->MaxTargetLevel;
MaxAffectedTargets = spellEntry->MaxAffectedTargets;
SpellFamilyName = spellEntry->SpellFamilyName;
diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h
index 6ce42ef52c..b0d9840813 100644
--- a/src/server/game/Spells/SpellInfo.h
+++ b/src/server/game/Spells/SpellInfo.h
@@ -236,7 +236,8 @@ private:
SpellTargetCheckTypes SelectionCheckType; // defines selection criteria
SpellTargetDirectionTypes DirectionType; // direction for cone and dest targets
};
- static StaticData _data[TOTAL_SPELL_TARGETS];
+
+ static std::array<StaticData, TOTAL_SPELL_TARGETS> _data;
};
class SpellEffectInfo
@@ -302,7 +303,8 @@ private:
SpellEffectImplicitTargetTypes ImplicitTargetType; // defines what target can be added to effect target list if there's no valid target type provided for effect
SpellTargetObjectTypes UsedTargetObjectType; // defines valid target object type for spell effect
};
- static StaticData _data[TOTAL_SPELL_EFFECTS];
+
+ static std::array<StaticData, TOTAL_SPELL_EFFECTS> _data;
};
class SpellInfo
@@ -360,18 +362,18 @@ public:
SpellRangeEntry const* RangeEntry;
float Speed;
uint32 StackAmount;
- uint32 Totem[2];
- int32 Reagent[MAX_SPELL_REAGENTS];
- uint32 ReagentCount[MAX_SPELL_REAGENTS];
+ std::array<uint32, 2> Totem;
+ std::array<int32, MAX_SPELL_REAGENTS> Reagent;
+ std::array<uint32, MAX_SPELL_REAGENTS> ReagentCount;
int32 EquippedItemClass;
int32 EquippedItemSubClassMask;
int32 EquippedItemInventoryTypeMask;
- uint32 TotemCategory[2];
- uint32 SpellVisual[2];
+ std::array<uint32, 2> TotemCategory;
+ std::array<uint32, 2> SpellVisual;
uint32 SpellIconID;
uint32 ActiveIconID;
- char* SpellName[16];
- char* Rank[16];
+ std::array<char const*, 16> SpellName;
+ std::array<char const*, 16> Rank;
uint32 MaxTargetLevel;
uint32 MaxAffectedTargets;
uint32 SpellFamilyName;
@@ -380,7 +382,7 @@ public:
uint32 PreventionType;
int32 AreaGroupId;
uint32 SchoolMask;
- SpellEffectInfo Effects[MAX_SPELL_EFFECTS];
+ std::array<SpellEffectInfo, MAX_SPELL_EFFECTS> Effects;
uint32 ExplicitTargetMask;
SpellChainNode const* ChainEntry;
diff --git a/src/server/game/World/IWorld.h b/src/server/game/World/IWorld.h
index 97c820e7bf..45649fe673 100644
--- a/src/server/game/World/IWorld.h
+++ b/src/server/game/World/IWorld.h
@@ -35,28 +35,22 @@ class WorldSession;
class Player;
/// Storage class for commands issued for delayed execution
-struct CliCommandHolder
+struct AC_GAME_API CliCommandHolder
{
- typedef void Print(void*, const char*);
- typedef void CommandFinished(void*, bool success);
+ using Print = void(*)(void*, std::string_view);
+ using CommandFinished = void(*)(void*, bool success);
void* m_callbackArg;
char* m_command;
- Print* m_print;
+ Print m_print;
+ CommandFinished m_commandFinished;
- CommandFinished* m_commandFinished;
+ CliCommandHolder(void* callbackArg, char const* command, Print zprint, CommandFinished commandFinished);
+ ~CliCommandHolder();
- CliCommandHolder(void* callbackArg, const char* command, Print* zprint, CommandFinished* commandFinished)
- : m_callbackArg(callbackArg), m_print(zprint), m_commandFinished(commandFinished)
- {
- // TODO: fix Codacy warning
- // "Does not handle strings that are not \0-terminated; if given one it may perform an over-read (it could cause a crash if unprotected) (CWE-126)."
- size_t len = strlen(command) + 1;
- m_command = new char[len];
- memcpy(m_command, command, len);
- }
-
- ~CliCommandHolder() { delete[] m_command; }
+private:
+ CliCommandHolder(CliCommandHolder const& right) = delete;
+ CliCommandHolder& operator=(CliCommandHolder const& right) = delete;
};
typedef std::unordered_map<uint32, WorldSession*> SessionMap;
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index 7f73365f6d..af9bdaf151 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -1944,6 +1944,9 @@ void World::SetInitialWorldSettings()
LOG_INFO("server.loading", " ");
sObjectMgr->InitializeSpellInfoPrecomputedData();
+ LOG_INFO("server.loading", "Initialize commands...");
+ Acore::ChatCommands::LoadCommandMap();
+
///- Initialize game time and timers
LOG_INFO("server.loading", "Initialize game time and timers");
LOG_INFO("server.loading", " ");
@@ -2760,7 +2763,7 @@ void World::UpdateSessions(uint32 diff)
// This handles the issued and queued CLI commands
void World::ProcessCliCommands()
{
- CliCommandHolder::Print* zprint = nullptr;
+ CliCommandHolder::Print zprint = nullptr;
void* callbackArg = nullptr;
CliCommandHolder* command = nullptr;
while (cliCmdQueue.next(command))
@@ -3505,3 +3508,13 @@ void World::FinalizePlayerWorldSession(WorldSession* session)
session->SendClientCacheVersion(cacheVersion);
session->SendTutorialsData();
}
+
+CliCommandHolder::CliCommandHolder(void* callbackArg, char const* command, Print zprint, CommandFinished commandFinished)
+ : m_callbackArg(callbackArg), m_command(strdup(command)), m_print(zprint), m_commandFinished(commandFinished)
+{
+}
+
+CliCommandHolder::~CliCommandHolder()
+{
+ free(m_command);
+}
diff --git a/src/server/scripts/Commands/cs_account.cpp b/src/server/scripts/Commands/cs_account.cpp
index 2b9dab60f7..cde632cb48 100644
--- a/src/server/scripts/Commands/cs_account.cpp
+++ b/src/server/scripts/Commands/cs_account.cpp
@@ -38,14 +38,20 @@ EndScriptData */
#include <openssl/rand.h>
#include <unordered_map>
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class account_commandscript : public CommandScript
{
public:
account_commandscript() : CommandScript("account_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> accountSetCommandTable =
+ static ChatCommandTable accountSetCommandTable =
{
{ "addon", SEC_GAMEMASTER, true, &HandleAccountSetAddonCommand, "" },
{ "gmlevel", SEC_CONSOLE, true, &HandleAccountSetGmLevelCommand, "" },
@@ -53,24 +59,24 @@ public:
{ "2fa", SEC_PLAYER, true, &HandleAccountSet2FACommand, "" }
};
- static std::vector<ChatCommand> accountLockCommandTable
+ static ChatCommandTable accountLockCommandTable
{
{ "country", SEC_PLAYER, true, &HandleAccountLockCountryCommand, "" },
{ "ip", SEC_PLAYER, true, &HandleAccountLockIpCommand, "" }
};
- static std::vector<ChatCommand> account2faCommandTable
+ static ChatCommandTable account2faCommandTable
{
{ "setup", SEC_PLAYER, false, &HandleAccount2FASetupCommand, "" },
{ "remove", SEC_PLAYER, false, &HandleAccount2FARemoveCommand, "" },
};
- static std::vector<ChatCommand> accountRemoveCommandTable
+ static ChatCommandTable accountRemoveCommandTable
{
{ "country", SEC_ADMINISTRATOR, true, &HandleAccountRemoveLockCountryCommand, "" }
};
- static std::vector<ChatCommand> accountCommandTable =
+ static ChatCommandTable accountCommandTable =
{
{ "2fa", SEC_PLAYER, true, nullptr, "", account2faCommandTable },
{ "addon", SEC_MODERATOR, false, &HandleAccountAddonCommand, "" },
@@ -84,7 +90,7 @@ public:
{ "", SEC_PLAYER, false, &HandleAccountCommand, "" }
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "account", SEC_PLAYER, true, nullptr, "", accountCommandTable }
};
diff --git a/src/server/scripts/Commands/cs_achievement.cpp b/src/server/scripts/Commands/cs_achievement.cpp
index 81b253a3ea..e2dcd36c93 100644
--- a/src/server/scripts/Commands/cs_achievement.cpp
+++ b/src/server/scripts/Commands/cs_achievement.cpp
@@ -27,19 +27,25 @@ EndScriptData */
#include "Player.h"
#include "ScriptMgr.h"
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class achievement_commandscript : public CommandScript
{
public:
achievement_commandscript() : CommandScript("achievement_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> achievementCommandTable =
+ static ChatCommandTable achievementCommandTable =
{
{ "add", SEC_GAMEMASTER, false, &HandleAchievementAddCommand, "" },
{ "checkall", SEC_ADMINISTRATOR, false, &HandleAchievementCheckAllCommand, "" }
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "achievement", SEC_GAMEMASTER, false, nullptr, "", achievementCommandTable }
};
diff --git a/src/server/scripts/Commands/cs_arena.cpp b/src/server/scripts/Commands/cs_arena.cpp
index 95fb28aa26..b24326695e 100644
--- a/src/server/scripts/Commands/cs_arena.cpp
+++ b/src/server/scripts/Commands/cs_arena.cpp
@@ -29,99 +29,73 @@ EndScriptData */
#include "Player.h"
#include "ScriptMgr.h"
+using namespace Acore::ChatCommands;
+
class arena_commandscript : public CommandScript
{
public:
arena_commandscript() : CommandScript("arena_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> arenaCommandTable =
- {
- { "create", SEC_ADMINISTRATOR, true, &HandleArenaCreateCommand, "" },
- { "disband", SEC_ADMINISTRATOR, true, &HandleArenaDisbandCommand, "" },
- { "rename", SEC_ADMINISTRATOR, true, &HandleArenaRenameCommand, "" },
- { "captain", SEC_ADMINISTRATOR, false, &HandleArenaCaptainCommand, "" },
- { "info", SEC_GAMEMASTER, true, &HandleArenaInfoCommand, "" },
- { "lookup", SEC_GAMEMASTER, false, &HandleArenaLookupCommand, "" },
+ static ChatCommandTable arenaCommandTable =
+ {
+ { "create", HandleArenaCreateCommand, SEC_ADMINISTRATOR, Console::Yes },
+ { "disband", HandleArenaDisbandCommand, SEC_ADMINISTRATOR, Console::Yes },
+ { "rename", HandleArenaRenameCommand, SEC_ADMINISTRATOR, Console::Yes },
+ { "captain", HandleArenaCaptainCommand, SEC_ADMINISTRATOR, Console::No },
+ { "info", HandleArenaInfoCommand, SEC_GAMEMASTER, Console::Yes },
+ { "lookup", HandleArenaLookupCommand, SEC_GAMEMASTER, Console::No },
};
- static std::vector<ChatCommand> commandTable =
+
+ static ChatCommandTable commandTable =
{
- { "arena", SEC_GAMEMASTER, false, nullptr, "", arenaCommandTable },
+ { "arena", arenaCommandTable }
};
+
return commandTable;
}
- static bool HandleArenaCreateCommand(ChatHandler* handler, char const* args)
+ static bool HandleArenaCreateCommand(ChatHandler* handler, Optional<PlayerIdentifier> captain, QuotedString name, ArenaTeamTypes type)
{
- if (!*args)
- return false;
-
- Player* target;
- if (!handler->extractPlayerTarget(*args != '"' ? (char*)args : nullptr, &target))
- return false;
-
- char* tailStr = *args != '"' ? strtok(nullptr, "") : (char*)args;
- if (!tailStr)
+ if (sArenaTeamMgr->GetArenaTeamByName(name))
+ {
+ handler->PSendSysMessage(LANG_ARENA_ERROR_NAME_EXISTS, name.c_str());
+ handler->SetSentErrorMessage(true);
return false;
+ }
- char* name = handler->extractQuotedArg(tailStr);
- if (!name)
- return false;
+ if (!captain)
+ captain = PlayerIdentifier::FromTargetOrSelf(handler);
- char* typeStr = strtok(nullptr, "");
- if (!typeStr)
+ if (!captain)
return false;
- int8 type = atoi(typeStr);
- if (sArenaTeamMgr->GetArenaTeamByName(name))
+ if (Player::GetArenaTeamIdFromDB(captain->GetGUID(), type) != 0)
{
- handler->PSendSysMessage(LANG_ARENA_ERROR_NAME_EXISTS, name);
+ handler->PSendSysMessage(LANG_ARENA_ERROR_SIZE, captain->GetName().c_str());
handler->SetSentErrorMessage(true);
return false;
}
- if (type == 2 || type == 3 || type == 5 )
- {
- if (Player::GetArenaTeamIdFromDB(target->GetGUID(), type) != 0)
- {
- handler->PSendSysMessage(LANG_ARENA_ERROR_SIZE, target->GetName().c_str());
- handler->SetSentErrorMessage(true);
- return false;
- }
+ ArenaTeam* arena = new ArenaTeam();
- ArenaTeam* arena = new ArenaTeam();
-
- if (!arena->Create(target->GetGUID(), type, name, 4293102085, 101, 4293253939, 4, 4284049911))
- {
- delete arena;
- handler->SendSysMessage(LANG_BAD_VALUE);
- handler->SetSentErrorMessage(true);
- return false;
- }
-
- sArenaTeamMgr->AddArenaTeam(arena);
- handler->PSendSysMessage(LANG_ARENA_CREATE, arena->GetName().c_str(), arena->GetId(), arena->GetType(), arena->GetCaptain());
- }
- else
+ if (!arena->Create(captain->GetGUID(), type, name, 4293102085, 101, 4293253939, 4, 4284049911))
{
+ delete arena;
handler->SendSysMessage(LANG_BAD_VALUE);
handler->SetSentErrorMessage(true);
return false;
}
+ sArenaTeamMgr->AddArenaTeam(arena);
+ handler->PSendSysMessage(LANG_ARENA_CREATE, arena->GetName().c_str(), arena->GetId(), arena->GetType(), arena->GetCaptain().GetCounter());
+
return true;
}
- static bool HandleArenaDisbandCommand(ChatHandler* handler, char const* args)
+ static bool HandleArenaDisbandCommand(ChatHandler* handler, uint32 teamId)
{
- if (!*args)
- return false;
-
- uint32 teamId = atoi((char*)args);
- if (!teamId)
- return false;
-
ArenaTeam* arena = sArenaTeamMgr->GetArenaTeamById(teamId);
if (!arena)
@@ -147,40 +121,19 @@ public:
return true;
}
- static bool HandleArenaRenameCommand(ChatHandler* handler, char const* _args)
+ static bool HandleArenaRenameCommand(ChatHandler* handler, QuotedString oldName, QuotedString newName)
{
- if (!*_args)
- return false;
-
- char* args = (char*)_args;
-
- char const* oldArenaStr = handler->extractQuotedArg(args);
- if (!oldArenaStr)
- {
- handler->SendSysMessage(LANG_BAD_VALUE);
- handler->SetSentErrorMessage(true);
- return false;
- }
-
- char const* newArenaStr = handler->extractQuotedArg(strtok(nullptr, ""));
- if (!newArenaStr)
- {
- handler->SendSysMessage(LANG_BAD_VALUE);
- handler->SetSentErrorMessage(true);
- return false;
- }
-
- ArenaTeam* arena = sArenaTeamMgr->GetArenaTeamByName(oldArenaStr);
+ ArenaTeam* arena = sArenaTeamMgr->GetArenaTeamByName(oldName);
if (!arena)
{
- handler->PSendSysMessage(LANG_AREAN_ERROR_NAME_NOT_FOUND, oldArenaStr);
+ handler->PSendSysMessage(LANG_AREAN_ERROR_NAME_NOT_FOUND, oldName.c_str());
handler->SetSentErrorMessage(true);
return false;
}
- if (sArenaTeamMgr->GetArenaTeamByName(newArenaStr))
+ if (sArenaTeamMgr->GetArenaTeamByName(newName))
{
- handler->PSendSysMessage(LANG_ARENA_ERROR_NAME_EXISTS, oldArenaStr);
+ handler->PSendSysMessage(LANG_ARENA_ERROR_NAME_EXISTS, oldName.c_str());
handler->SetSentErrorMessage(true);
return false;
}
@@ -192,40 +145,21 @@ public:
return false;
}
- if (!arena->SetName(newArenaStr))
+ if (!arena->SetName(newName))
{
handler->SendSysMessage(LANG_BAD_VALUE);
handler->SetSentErrorMessage(true);
return false;
}
- handler->PSendSysMessage(LANG_ARENA_RENAME, arena->GetId(), oldArenaStr, newArenaStr);
+ handler->PSendSysMessage(LANG_ARENA_RENAME, arena->GetId(), oldName.c_str(), newName.c_str());
return true;
}
- static bool HandleArenaCaptainCommand(ChatHandler* handler, char const* args)
+ static bool HandleArenaCaptainCommand(ChatHandler* handler, uint32 teamId, Optional<PlayerIdentifier> target)
{
- if (!*args)
- return false;
-
- char* idStr;
- char* nameStr;
- handler->extractOptFirstArg((char*)args, &idStr, &nameStr);
- if (!idStr)
- return false;
-
- uint32 teamId = atoi(idStr);
- if (!teamId)
- return false;
-
- Player* target;
- ObjectGuid targetGuid;
- if (!handler->extractPlayerTarget(nameStr, &target, &targetGuid))
- return false;
-
ArenaTeam* arena = sArenaTeamMgr->GetArenaTeamById(teamId);
-
if (!arena)
{
handler->PSendSysMessage(LANG_ARENA_ERROR_NOT_FOUND, teamId);
@@ -233,13 +167,6 @@ public:
return false;
}
- if (!target)
- {
- handler->PSendSysMessage(LANG_PLAYER_NOT_EXIST_OR_OFFLINE, nameStr);
- handler->SetSentErrorMessage(true);
- return false;
- }
-
if (arena->IsFighting())
{
handler->SendSysMessage(LANG_ARENA_ERROR_COMBAT);
@@ -247,40 +174,38 @@ public:
return false;
}
- if (!arena->IsMember(targetGuid))
+ if (!target)
+ target = PlayerIdentifier::FromTargetOrSelf(handler);
+
+ if (!target)
+ return false;
+
+ if (!arena->IsMember(target->GetGUID()))
{
- handler->PSendSysMessage(LANG_ARENA_ERROR_NOT_MEMBER, nameStr, arena->GetName().c_str());
+ handler->PSendSysMessage(LANG_ARENA_ERROR_NOT_MEMBER, target->GetName().c_str(), arena->GetName().c_str());
handler->SetSentErrorMessage(true);
return false;
}
- if (arena->GetCaptain() == targetGuid)
+ if (arena->GetCaptain() == target->GetGUID())
{
- handler->PSendSysMessage(LANG_ARENA_ERROR_CAPTAIN, nameStr, arena->GetName().c_str());
+ handler->PSendSysMessage(LANG_ARENA_ERROR_CAPTAIN, target->GetName().c_str(), arena->GetName().c_str());
handler->SetSentErrorMessage(true);
return false;
}
std::string oldCaptainName;
sObjectMgr->GetPlayerNameByGUID(arena->GetCaptain().GetCounter(), oldCaptainName);
- arena->SetCaptain(targetGuid);
+ arena->SetCaptain(target->GetGUID());
handler->PSendSysMessage(LANG_ARENA_CAPTAIN, arena->GetName().c_str(), arena->GetId(), oldCaptainName.c_str(), target->GetName().c_str());
return true;
}
- static bool HandleArenaInfoCommand(ChatHandler* handler, char const* args)
+ static bool HandleArenaInfoCommand(ChatHandler* handler, uint32 teamId)
{
- if (!*args)
- return false;
-
- uint32 teamId = atoi((char*)args);
- if (!teamId)
- return false;
-
ArenaTeam* arena = sArenaTeamMgr->GetArenaTeamById(teamId);
-
if (!arena)
{
handler->PSendSysMessage(LANG_ARENA_ERROR_NOT_FOUND, teamId);
@@ -289,36 +214,26 @@ public:
}
handler->PSendSysMessage(LANG_ARENA_INFO_HEADER, arena->GetName().c_str(), arena->GetId(), arena->GetRating(), arena->GetType(), arena->GetType());
- for (ArenaTeam::MemberList::iterator itr = arena->m_membersBegin(); itr != arena->m_membersEnd(); ++itr)
- handler->PSendSysMessage(LANG_ARENA_INFO_MEMBERS, itr->Name.c_str(), itr->Guid.GetCounter(), itr->PersonalRating, (arena->GetCaptain() == itr->Guid ? "- Captain" : ""));
+
+ for (auto const& itr : arena->GetMembers())
+ handler->PSendSysMessage(LANG_ARENA_INFO_MEMBERS, itr.Name.c_str(), itr.Guid.GetCounter(), itr.PersonalRating, (arena->GetCaptain() == itr.Guid ? "- Captain" : ""));
return true;
}
- static bool HandleArenaLookupCommand(ChatHandler* handler, char const* args)
+ static bool HandleArenaLookupCommand(ChatHandler* handler, Tail needle)
{
- if (!*args)
+ if (needle.empty())
return false;
- std::string namepart = args;
- std::wstring wnamepart;
-
- if (!Utf8toWStr(namepart, wnamepart))
- return false;
-
- wstrToLower(wnamepart);
-
bool found = false;
- ArenaTeamMgr::ArenaTeamContainer::const_iterator i = sArenaTeamMgr->GetArenaTeamMapBegin();
- for (; i != sArenaTeamMgr->GetArenaTeamMapEnd(); ++i)
+ for (auto [teamId, team] : sArenaTeamMgr->GetArenaTeams())
{
- ArenaTeam* arena = i->second;
-
- if (Utf8FitTo(arena->GetName(), wnamepart))
+ if (StringContainsStringI(team->GetName(), needle))
{
if (handler->GetSession())
{
- handler->PSendSysMessage(LANG_ARENA_LOOKUP, arena->GetName().c_str(), arena->GetId(), arena->GetType(), arena->GetType());
+ handler->PSendSysMessage(LANG_ARENA_LOOKUP, team->GetName().c_str(), team->GetId(), team->GetType(), team->GetType());
found = true;
continue;
}
@@ -326,7 +241,7 @@ public:
}
if (!found)
- handler->PSendSysMessage(LANG_AREAN_ERROR_NAME_NOT_FOUND, namepart.c_str());
+ handler->PSendSysMessage(LANG_AREAN_ERROR_NAME_NOT_FOUND, std::string(needle).c_str());
return true;
}
diff --git a/src/server/scripts/Commands/cs_ban.cpp b/src/server/scripts/Commands/cs_ban.cpp
index 9172a8c7e5..9a09c62a98 100644
--- a/src/server/scripts/Commands/cs_ban.cpp
+++ b/src/server/scripts/Commands/cs_ban.cpp
@@ -39,36 +39,42 @@ enum BanMode
BAN_IP
};
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class ban_commandscript : public CommandScript
{
public:
ban_commandscript() : CommandScript("ban_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> unbanCommandTable =
+ static ChatCommandTable unbanCommandTable =
{
- { "account", SEC_GAMEMASTER, true, &HandleUnBanAccountCommand, "" },
- { "character", SEC_GAMEMASTER, true, &HandleUnBanCharacterCommand, "" },
- { "playeraccount", SEC_GAMEMASTER, true, &HandleUnBanAccountByCharCommand, "" },
- { "ip", SEC_GAMEMASTER, true, &HandleUnBanIPCommand, "" }
+ { "account", SEC_ADMINISTRATOR, true, &HandleUnBanAccountCommand, "" },
+ { "character", SEC_ADMINISTRATOR, true, &HandleUnBanCharacterCommand, "" },
+ { "playeraccount", SEC_ADMINISTRATOR, true, &HandleUnBanAccountByCharCommand, "" },
+ { "ip", SEC_ADMINISTRATOR, true, &HandleUnBanIPCommand, "" }
};
- static std::vector<ChatCommand> banlistCommandTable =
+ static ChatCommandTable banlistCommandTable =
{
{ "account", SEC_GAMEMASTER, true, &HandleBanListAccountCommand, "" },
{ "character", SEC_GAMEMASTER, true, &HandleBanListCharacterCommand, "" },
{ "ip", SEC_GAMEMASTER, true, &HandleBanListIPCommand, "" }
};
- static std::vector<ChatCommand> baninfoCommandTable =
+ static ChatCommandTable baninfoCommandTable =
{
{ "account", SEC_GAMEMASTER, true, &HandleBanInfoAccountCommand, "" },
{ "character", SEC_GAMEMASTER, true, &HandleBanInfoCharacterCommand, "" },
{ "ip", SEC_GAMEMASTER, true, &HandleBanInfoIPCommand, "" }
};
- static std::vector<ChatCommand> banCommandTable =
+ static ChatCommandTable banCommandTable =
{
{ "account", SEC_GAMEMASTER, true, &HandleBanAccountCommand, "" },
{ "character", SEC_GAMEMASTER, true, &HandleBanCharacterCommand, "" },
@@ -76,12 +82,12 @@ public:
{ "ip", SEC_GAMEMASTER, true, &HandleBanIPCommand, "" }
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
- { "ban", SEC_GAMEMASTER, true, nullptr, "", banCommandTable },
- { "baninfo", SEC_GAMEMASTER, true, nullptr, "", baninfoCommandTable },
- { "banlist", SEC_GAMEMASTER, true, nullptr, "", banlistCommandTable },
- { "unban", SEC_GAMEMASTER, true, nullptr, "", unbanCommandTable }
+ { "ban", SEC_GAMEMASTER, true, nullptr, "", banCommandTable },
+ { "baninfo", SEC_GAMEMASTER, true, nullptr, "", baninfoCommandTable },
+ { "banlist", SEC_GAMEMASTER, true, nullptr, "", banlistCommandTable },
+ { "unban", SEC_ADMINISTRATOR, true, nullptr, "", unbanCommandTable }
};
return commandTable;
diff --git a/src/server/scripts/Commands/cs_bf.cpp b/src/server/scripts/Commands/cs_bf.cpp
index cb2ed92968..c38710f760 100644
--- a/src/server/scripts/Commands/cs_bf.cpp
+++ b/src/server/scripts/Commands/cs_bf.cpp
@@ -26,14 +26,20 @@ EndScriptData */
#include "Chat.h"
#include "ScriptMgr.h"
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class bf_commandscript : public CommandScript
{
public:
bf_commandscript() : CommandScript("bf_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> battlefieldcommandTable =
+ static ChatCommandTable battlefieldcommandTable =
{
{ "start", SEC_ADMINISTRATOR, false, &HandleBattlefieldStart, "" },
{ "stop", SEC_ADMINISTRATOR, false, &HandleBattlefieldEnd, "" },
@@ -41,7 +47,7 @@ public:
{ "timer", SEC_ADMINISTRATOR, false, &HandleBattlefieldTimer, "" },
{ "enable", SEC_ADMINISTRATOR, false, &HandleBattlefieldEnable, "" }
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "bf", SEC_ADMINISTRATOR, false, nullptr, "", battlefieldcommandTable }
};
diff --git a/src/server/scripts/Commands/cs_cast.cpp b/src/server/scripts/Commands/cs_cast.cpp
index 1f68a8b576..5c56775b53 100644
--- a/src/server/scripts/Commands/cs_cast.cpp
+++ b/src/server/scripts/Commands/cs_cast.cpp
@@ -29,14 +29,20 @@ EndScriptData */
#include "ScriptMgr.h"
#include "SpellInfo.h"
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class cast_commandscript : public CommandScript
{
public:
cast_commandscript() : CommandScript("cast_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> castCommandTable =
+ static ChatCommandTable castCommandTable =
{
{ "back", SEC_GAMEMASTER, false, &HandleCastBackCommand, "" },
{ "dist", SEC_GAMEMASTER, false, &HandleCastDistCommand, "" },
@@ -45,7 +51,7 @@ public:
{ "dest", SEC_GAMEMASTER, false, &HandleCastDestCommand, "" },
{ "", SEC_GAMEMASTER, false, &HandleCastCommand, "" }
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "cast", SEC_GAMEMASTER, false, nullptr, "", castCommandTable }
};
diff --git a/src/server/scripts/Commands/cs_character.cpp b/src/server/scripts/Commands/cs_character.cpp
index 77950b5b65..4bb6228bbd 100644
--- a/src/server/scripts/Commands/cs_character.cpp
+++ b/src/server/scripts/Commands/cs_character.cpp
@@ -31,54 +31,61 @@ EndScriptData */
#include "ReputationMgr.h"
#include "ScriptMgr.h"
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class character_commandscript : public CommandScript
{
public:
character_commandscript() : CommandScript("character_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> pdumpCommandTable =
+ static ChatCommandTable pdumpCommandTable =
{
- { "load", SEC_ADMINISTRATOR, true, &HandlePDumpLoadCommand, "" },
- { "write", SEC_ADMINISTRATOR, true, &HandlePDumpWriteCommand, "" }
+ { "load", HandlePDumpLoadCommand, SEC_ADMINISTRATOR, Console::Yes },
+ { "write", HandlePDumpWriteCommand, SEC_ADMINISTRATOR, Console::Yes }
};
- static std::vector<ChatCommand> characterDeletedCommandTable =
+ static ChatCommandTable characterDeletedCommandTable =
{
- { "delete", SEC_CONSOLE, true, &HandleCharacterDeletedDeleteCommand, "" },
- { "list", SEC_ADMINISTRATOR, true, &HandleCharacterDeletedListCommand, "" },
- { "restore", SEC_ADMINISTRATOR, true, &HandleCharacterDeletedRestoreCommand, "" },
- { "purge", SEC_CONSOLE, true, &HandleCharacterDeletedPurgeCommand, "" },
+ { "delete", HandleCharacterDeletedDeleteCommand, SEC_CONSOLE, Console::Yes },
+ { "list", HandleCharacterDeletedListCommand, SEC_ADMINISTRATOR, Console::Yes },
+ { "restore", HandleCharacterDeletedRestoreCommand, SEC_ADMINISTRATOR, Console::Yes },
+ { "purge", HandleCharacterDeletedPurgeCommand, SEC_CONSOLE, Console::Yes }
};
- static std::vector<ChatCommand> characterCheckCommandTable =
+ static ChatCommandTable characterCheckCommandTable =
{
- { "bank", SEC_GAMEMASTER, false, &HandleCharacterCheckBankCommand, "" },
- { "bag", SEC_GAMEMASTER, false, &HandleCharacterCheckBagCommand, "" },
- { "profession", SEC_GAMEMASTER, false, &HandleCharacterCheckProfessionCommand, "" },
+ { "bank", HandleCharacterCheckBankCommand, SEC_GAMEMASTER, Console::Yes },
+ { "bag", HandleCharacterCheckBagCommand, SEC_GAMEMASTER, Console::Yes },
+ { "profession", HandleCharacterCheckProfessionCommand, SEC_GAMEMASTER, Console::Yes }
};
- static std::vector<ChatCommand> characterCommandTable =
+ static ChatCommandTable characterCommandTable =
{
- { "customize", SEC_GAMEMASTER, true, &HandleCharacterCustomizeCommand, "" },
- { "changefaction", SEC_GAMEMASTER, true, &HandleCharacterChangeFactionCommand, "" },
- { "changerace", SEC_GAMEMASTER, true, &HandleCharacterChangeRaceCommand, "" },
- { "check", SEC_GAMEMASTER, false, nullptr, "", characterCheckCommandTable },
- { "erase", SEC_CONSOLE, true, &HandleCharacterEraseCommand, "" },
- { "deleted", SEC_ADMINISTRATOR, true, nullptr, "", characterDeletedCommandTable },
- { "level", SEC_GAMEMASTER, true, &HandleCharacterLevelCommand, "" },
- { "rename", SEC_GAMEMASTER, true, &HandleCharacterRenameCommand, "" },
- { "reputation", SEC_GAMEMASTER, true, &HandleCharacterReputationCommand, "" },
- { "titles", SEC_GAMEMASTER, true, &HandleCharacterTitlesCommand, "" }
+ { "customize", HandleCharacterCustomizeCommand, SEC_GAMEMASTER, Console::Yes },
+ { "changefaction", HandleCharacterChangeFactionCommand, SEC_GAMEMASTER, Console::Yes },
+ { "changerace", HandleCharacterChangeRaceCommand, SEC_GAMEMASTER, Console::Yes },
+ { "check", characterCheckCommandTable },
+ { "erase", HandleCharacterEraseCommand, SEC_CONSOLE, Console::Yes },
+ { "deleted", characterDeletedCommandTable },
+ { "level", HandleCharacterLevelCommand, SEC_GAMEMASTER, Console::Yes },
+ { "rename", HandleCharacterRenameCommand, SEC_GAMEMASTER, Console::Yes },
+ { "reputation", HandleCharacterReputationCommand, SEC_GAMEMASTER, Console::Yes },
+ { "titles", HandleCharacterTitlesCommand, SEC_GAMEMASTER, Console::Yes }
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
- { "character", SEC_GAMEMASTER, true, nullptr, "", characterCommandTable },
- { "levelup", SEC_GAMEMASTER, false, &HandleLevelUpCommand, "" },
- { "pdump", SEC_ADMINISTRATOR, true, nullptr, "", pdumpCommandTable }
+ { "character", characterCommandTable },
+ { "levelup", HandleLevelUpCommand, SEC_GAMEMASTER, Console::No },
+ { "pdump", pdumpCommandTable }
};
+
return commandTable;
}
@@ -222,7 +229,7 @@ public:
return;
}
- CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_RESTORE_DELETE_INFO);
+ auto* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_RESTORE_DELETE_INFO);
stmt->setString(0, delInfo.name);
stmt->setUInt32(1, delInfo.accountId);
stmt->setUInt32(2, delInfo.lowGuid);
@@ -255,7 +262,7 @@ public:
else
{
// Update level and reset XP, everything else will be updated at login
- CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_LEVEL);
+ auto* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_LEVEL);
stmt->setUInt8(0, uint8(newLevel));
stmt->setUInt32(1, playerGuid.GetCounter());
CharacterDatabase.Execute(stmt);
@@ -347,7 +354,7 @@ public:
std::string oldNameLink = handler->playerLink(targetName);
handler->PSendSysMessage(LANG_RENAME_PLAYER_GUID, oldNameLink.c_str(), targetGuid.GetCounter());
- CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG);
+ auto* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG);
stmt->setUInt16(0, uint16(AT_LOGIN_RENAME));
stmt->setUInt32(1, targetGuid.GetCounter());
CharacterDatabase.Execute(stmt);
@@ -361,29 +368,15 @@ public:
return true;
}
- static bool HandleCharacterLevelCommand(ChatHandler* handler, char const* args)
+ static bool HandleCharacterLevelCommand(ChatHandler* handler, Optional<PlayerIdentifier> player, int16 newlevel)
{
- char* nameStr;
- char* levelStr;
- handler->extractOptFirstArg((char*)args, &nameStr, &levelStr);
- if (!levelStr)
- return false;
-
- // exception opt second arg: .character level $name
- if (isalpha(levelStr[0]))
- {
- nameStr = levelStr;
- levelStr = nullptr; // current level will used
- }
+ if (!player)
+ player = PlayerIdentifier::FromTargetOrSelf(handler);
- Player* target;
- ObjectGuid targetGuid;
- std::string targetName;
- if (!handler->extractPlayerTarget(nameStr, &target, &targetGuid, &targetName))
+ if (!player)
return false;
- int32 oldlevel = target ? target->getLevel() : Player::GetLevelFromStorage(targetGuid.GetCounter());
- int32 newlevel = levelStr ? atoi(levelStr) : oldlevel;
+ uint8 oldlevel = player->IsConnected() ? player->GetConnectedPlayer()->getLevel() : static_cast<uint8>(Player::GetLevelFromStorage(player->GetGUID().GetCounter()));
if (newlevel < 1)
return false; // invalid level
@@ -391,12 +384,10 @@ public:
if (newlevel > DEFAULT_MAX_LEVEL) // hardcoded maximum level
newlevel = DEFAULT_MAX_LEVEL;
- HandleCharacterLevel(target, targetGuid, oldlevel, newlevel, handler);
- if (!handler->GetSession() || handler->GetSession()->GetPlayer() != target) // including player == nullptr
- {
- std::string nameLink = handler->playerLink(targetName);
- handler->PSendSysMessage(LANG_YOU_CHANGE_LVL, nameLink.c_str(), newlevel);
- }
+ HandleCharacterLevel(player->GetConnectedPlayer(), player->GetGUID(), oldlevel, newlevel, handler);
+
+ if (!handler->GetSession() || (handler->GetSession()->GetPlayer() != player->GetConnectedPlayer())) // including chr == NULL
+ handler->PSendSysMessage(LANG_YOU_CHANGE_LVL, handler->playerLink(*player).c_str(), newlevel);
return true;
}
@@ -410,7 +401,7 @@ public:
if (!handler->extractPlayerTarget((char*)args, &target, &targetGuid, &targetName))
return false;
- CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG);
+ auto* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG);
stmt->setUInt16(0, uint16(AT_LOGIN_CUSTOMIZE));
if (target)
{
@@ -438,7 +429,7 @@ public:
if (!handler->extractPlayerTarget((char*)args, &target, &targetGuid, &targetName))
return false;
- CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG);
+ auto* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG);
stmt->setUInt16(0, uint16(AT_LOGIN_CHANGE_FACTION));
if (target)
{
@@ -465,7 +456,7 @@ public:
if (!handler->extractPlayerTarget((char*)args, &target, &targetGuid, &targetName))
return false;
- CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG);
+ auto* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG);
stmt->setUInt16(0, uint16(AT_LOGIN_CHANGE_RACE));
if (target)
{
@@ -745,28 +736,16 @@ public:
return true;
}
- static bool HandleLevelUpCommand(ChatHandler* handler, char const* args)
+ static bool HandleLevelUpCommand(ChatHandler* handler, Optional<PlayerIdentifier> player, int16 level)
{
- char* nameStr;
- char* levelStr;
- handler->extractOptFirstArg((char*)args, &nameStr, &levelStr);
-
- // exception opt second arg: .character level $name
- if (levelStr && isalpha(levelStr[0]))
- {
- nameStr = levelStr;
- levelStr = nullptr; // current level will used
- }
+ if (!player)
+ player = PlayerIdentifier::FromTargetOrSelf(handler);
- Player* target;
- ObjectGuid targetGuid;
- std::string targetName;
- if (!handler->extractPlayerTarget(nameStr, &target, &targetGuid, &targetName))
+ if (!player)
return false;
- int32 oldlevel = target ? target->getLevel() : Player::GetLevelFromStorage(targetGuid.GetCounter());
- int32 addlevel = levelStr ? atoi(levelStr) : 1;
- int32 newlevel = oldlevel + addlevel;
+ uint8 oldlevel = static_cast<uint8>(player->IsConnected() ? player->GetConnectedPlayer()->getLevel() : Player::GetLevelFromStorage(player->GetGUID().GetCounter()));
+ int16 newlevel = static_cast<int16>(oldlevel) + level;
if (newlevel < 1)
newlevel = 1;
@@ -774,13 +753,10 @@ public:
if (newlevel > STRONG_MAX_LEVEL) // hardcoded maximum level
newlevel = STRONG_MAX_LEVEL;
- HandleCharacterLevel(target, targetGuid, oldlevel, newlevel, handler);
+ HandleCharacterLevel(player->GetConnectedPlayer(), player->GetGUID(), oldlevel, newlevel, handler);
- if (!handler->GetSession() || handler->GetSession()->GetPlayer() != target) // including chr == nullptr
- {
- std::string nameLink = handler->playerLink(targetName);
- handler->PSendSysMessage(LANG_YOU_CHANGE_LVL, nameLink.c_str(), newlevel);
- }
+ if (!handler->GetSession() || (handler->GetSession()->GetPlayer() != player->GetConnectedPlayer())) // including chr == NULL
+ handler->PSendSysMessage(LANG_YOU_CHANGE_LVL, handler->playerLink(*player).c_str(), newlevel);
return true;
}
diff --git a/src/server/scripts/Commands/cs_cheat.cpp b/src/server/scripts/Commands/cs_cheat.cpp
index c3a8efe3e1..6a34e2e3de 100644
--- a/src/server/scripts/Commands/cs_cheat.cpp
+++ b/src/server/scripts/Commands/cs_cheat.cpp
@@ -20,14 +20,20 @@
#include "Player.h"
#include "ScriptMgr.h"
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class cheat_commandscript : public CommandScript
{
public:
cheat_commandscript() : CommandScript("cheat_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> cheatCommandTable =
+ static ChatCommandTable cheatCommandTable =
{
{ "god", SEC_GAMEMASTER, false, &HandleGodModeCheatCommand, "" },
{ "casttime", SEC_GAMEMASTER, false, &HandleCasttimeCheatCommand, "" },
@@ -39,7 +45,7 @@ public:
{ "explore", SEC_GAMEMASTER, false, &HandleExploreCheatCommand, "" },
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "cheat", SEC_GAMEMASTER, false, nullptr, "", cheatCommandTable },
};
diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp
index 169fcfccf2..32535e86f3 100644
--- a/src/server/scripts/Commands/cs_debug.cpp
+++ b/src/server/scripts/Commands/cs_debug.cpp
@@ -34,20 +34,26 @@ EndScriptData */
#include "ScriptMgr.h"
#include <fstream>
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class debug_commandscript : public CommandScript
{
public:
debug_commandscript() : CommandScript("debug_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> debugPlayCommandTable =
+ static ChatCommandTable debugPlayCommandTable =
{
{ "cinematic", SEC_MODERATOR, false, &HandleDebugPlayCinematicCommand, "" },
{ "movie", SEC_MODERATOR, false, &HandleDebugPlayMovieCommand, "" },
{ "sound", SEC_MODERATOR, false, &HandleDebugPlaySoundCommand, "" }
};
- static std::vector<ChatCommand> debugSendCommandTable =
+ static ChatCommandTable debugSendCommandTable =
{
{ "buyerror", SEC_ADMINISTRATOR, false, &HandleDebugSendBuyErrorCommand, "" },
{ "channelnotify", SEC_ADMINISTRATOR, false, &HandleDebugSendChannelNotifyCommand, "" },
@@ -61,7 +67,7 @@ public:
{ "setphaseshift", SEC_ADMINISTRATOR, false, &HandleDebugSendSetPhaseShiftCommand, "" },
{ "spellfail", SEC_ADMINISTRATOR, false, &HandleDebugSendSpellFailCommand, "" }
};
- static std::vector<ChatCommand> debugCommandTable =
+ static ChatCommandTable debugCommandTable =
{
{ "setbit", SEC_ADMINISTRATOR, false, &HandleDebugSet32BitCommand, "" },
{ "threat", SEC_ADMINISTRATOR, false, &HandleDebugThreatListCommand, "" },
@@ -91,7 +97,7 @@ public:
{ "moveflags", SEC_ADMINISTRATOR, false, &HandleDebugMoveflagsCommand, "" },
{ "unitstate", SEC_ADMINISTRATOR, false, &HandleDebugUnitStateCommand, "" }
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "debug", SEC_GAMEMASTER, true, nullptr, "", debugCommandTable },
{ "wpgps", SEC_ADMINISTRATOR, false, &HandleWPGPSCommand, "", }
diff --git a/src/server/scripts/Commands/cs_deserter.cpp b/src/server/scripts/Commands/cs_deserter.cpp
index e2af2a643e..30bb9334a9 100644
--- a/src/server/scripts/Commands/cs_deserter.cpp
+++ b/src/server/scripts/Commands/cs_deserter.cpp
@@ -28,32 +28,38 @@ enum Spells
BG_SPELL_DESERTER = 26013
};
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class deserter_commandscript : public CommandScript
{
public:
deserter_commandscript() : CommandScript("deserter_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> deserterinstanceCommandTable =
+ static ChatCommandTable deserterinstanceCommandTable =
{
{ "add", SEC_ADMINISTRATOR, false, &HandleDeserterInstanceAdd, "" },
{ "remove", SEC_ADMINISTRATOR, false, &HandleDeserterInstanceRemove, "" }
};
- static std::vector<ChatCommand> deserterBGCommandTable =
+ static ChatCommandTable deserterBGCommandTable =
{
{ "add", SEC_ADMINISTRATOR, false, &HandleDeserterBGAdd, "" },
{ "remove", SEC_ADMINISTRATOR, false, &HandleDeserterBGRemove, "" }
};
- static std::vector<ChatCommand> deserterCommandTable =
+ static ChatCommandTable deserterCommandTable =
{
{ "instance", SEC_ADMINISTRATOR, false, nullptr, "", deserterinstanceCommandTable },
{ "bg", SEC_ADMINISTRATOR, false, nullptr, "", deserterBGCommandTable }
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "deserter", SEC_ADMINISTRATOR, false, nullptr, "", deserterCommandTable }
};
diff --git a/src/server/scripts/Commands/cs_disable.cpp b/src/server/scripts/Commands/cs_disable.cpp
index e1548a5489..60104a49f4 100644
--- a/src/server/scripts/Commands/cs_disable.cpp
+++ b/src/server/scripts/Commands/cs_disable.cpp
@@ -32,14 +32,20 @@ EndScriptData */
#include "ScriptMgr.h"
#include "SpellMgr.h"
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class disable_commandscript : public CommandScript
{
public:
disable_commandscript() : CommandScript("disable_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> removeDisableCommandTable =
+ static ChatCommandTable removeDisableCommandTable =
{
{ "spell", SEC_ADMINISTRATOR, true, &HandleRemoveDisableSpellCommand, "" },
{ "quest", SEC_ADMINISTRATOR, true, &HandleRemoveDisableQuestCommand, "" },
@@ -48,7 +54,7 @@ public:
{ "outdoorpvp", SEC_ADMINISTRATOR, true, &HandleRemoveDisableOutdoorPvPCommand, "" },
{ "vmap", SEC_ADMINISTRATOR, true, &HandleRemoveDisableVmapCommand, "" },
};
- static std::vector<ChatCommand> addDisableCommandTable =
+ static ChatCommandTable addDisableCommandTable =
{
{ "spell", SEC_ADMINISTRATOR, true, &HandleAddDisableSpellCommand, "" },
{ "quest", SEC_ADMINISTRATOR, true, &HandleAddDisableQuestCommand, "" },
@@ -57,12 +63,12 @@ public:
{ "outdoorpvp", SEC_ADMINISTRATOR, true, &HandleAddDisableOutdoorPvPCommand, "" },
{ "vmap", SEC_ADMINISTRATOR, true, &HandleAddDisableVmapCommand, "" },
};
- static std::vector<ChatCommand> disableCommandTable =
+ static ChatCommandTable disableCommandTable =
{
{ "add", SEC_ADMINISTRATOR, true, nullptr, "", addDisableCommandTable },
{ "remove", SEC_ADMINISTRATOR, true, nullptr, "", removeDisableCommandTable },
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "disable", SEC_ADMINISTRATOR, false, nullptr, "", disableCommandTable },
};
diff --git a/src/server/scripts/Commands/cs_event.cpp b/src/server/scripts/Commands/cs_event.cpp
index 2df1bb4c84..9ae1577814 100644
--- a/src/server/scripts/Commands/cs_event.cpp
+++ b/src/server/scripts/Commands/cs_event.cpp
@@ -28,21 +28,27 @@ EndScriptData */
#include "Player.h"
#include "ScriptMgr.h"
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class event_commandscript : public CommandScript
{
public:
event_commandscript() : CommandScript("event_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> eventCommandTable =
+ static ChatCommandTable eventCommandTable =
{
{ "activelist", SEC_GAMEMASTER, true, &HandleEventActiveListCommand, "" },
{ "start", SEC_GAMEMASTER, true, &HandleEventStartCommand, "" },
{ "stop", SEC_GAMEMASTER, true, &HandleEventStopCommand, "" },
{ "", SEC_GAMEMASTER, true, &HandleEventInfoCommand, "" }
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "event", SEC_GAMEMASTER, false, nullptr, "", eventCommandTable }
};
diff --git a/src/server/scripts/Commands/cs_gm.cpp b/src/server/scripts/Commands/cs_gm.cpp
index 8f7a731bc2..e8bf762dcf 100644
--- a/src/server/scripts/Commands/cs_gm.cpp
+++ b/src/server/scripts/Commands/cs_gm.cpp
@@ -32,23 +32,29 @@ EndScriptData */
#include "ScriptMgr.h"
#include "World.h"
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class gm_commandscript : public CommandScript
{
public:
gm_commandscript() : CommandScript("gm_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> gmCommandTable =
+ static ChatCommandTable gmCommandTable =
{
{ "chat", SEC_GAMEMASTER, false, &HandleGMChatCommand, "" },
{ "fly", SEC_GAMEMASTER, false, &HandleGMFlyCommand, "" },
{ "ingame", SEC_PLAYER, true, &HandleGMListIngameCommand, "" },
{ "list", SEC_GAMEMASTER, true, &HandleGMListFullCommand, "" },
{ "visible", SEC_GAMEMASTER, false, &HandleGMVisibleCommand, "" },
- { "", SEC_GAMEMASTER, false, &HandleGMCommand, "" }
+ { "", SEC_MODERATOR, false, &HandleGMCommand, "" }
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "gm", SEC_MODERATOR, false, nullptr, "", gmCommandTable }
};
diff --git a/src/server/scripts/Commands/cs_go.cpp b/src/server/scripts/Commands/cs_go.cpp
index 91cb8619ef..8b6caa437c 100644
--- a/src/server/scripts/Commands/cs_go.cpp
+++ b/src/server/scripts/Commands/cs_go.cpp
@@ -31,14 +31,20 @@ EndScriptData */
#include "ScriptMgr.h"
#include "TicketMgr.h"
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class go_commandscript : public CommandScript
{
public:
go_commandscript() : CommandScript("go_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> goCommandTable =
+ static ChatCommandTable goCommandTable =
{
{ "creature", SEC_MODERATOR, false, &HandleGoCreatureCommand, "" },
{ "graveyard", SEC_MODERATOR, false, &HandleGoGraveyardCommand, "" },
@@ -53,7 +59,7 @@ public:
{ "", SEC_MODERATOR, false, &HandleGoXYZCommand, "" }
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "go", SEC_MODERATOR, false, nullptr, "", goCommandTable }
};
diff --git a/src/server/scripts/Commands/cs_gobject.cpp b/src/server/scripts/Commands/cs_gobject.cpp
index b7c6ec3abd..2d30e37dd4 100644
--- a/src/server/scripts/Commands/cs_gobject.cpp
+++ b/src/server/scripts/Commands/cs_gobject.cpp
@@ -34,26 +34,32 @@ EndScriptData */
#include "ScriptMgr.h"
#include "Transport.h"
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class gobject_commandscript : public CommandScript
{
public:
gobject_commandscript() : CommandScript("gobject_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> gobjectAddCommandTable =
+ static ChatCommandTable gobjectAddCommandTable =
{
{ "temp", SEC_GAMEMASTER, false, &HandleGameObjectAddTempCommand, "" },
{ "", SEC_ADMINISTRATOR, false, &HandleGameObjectAddCommand, "" }
};
- static std::vector<ChatCommand> gobjectSetCommandTable =
+ static ChatCommandTable gobjectSetCommandTable =
{
{ "phase", SEC_ADMINISTRATOR, false, &HandleGameObjectSetPhaseCommand, "" },
{ "state", SEC_ADMINISTRATOR, false, &HandleGameObjectSetStateCommand, "" }
};
- static std::vector<ChatCommand> gobjectCommandTable =
+ static ChatCommandTable gobjectCommandTable =
{
- { "activate", SEC_ADMINISTRATOR, false, &HandleGameObjectActivateCommand, "" },
+ { "activate", SEC_GAMEMASTER, false, &HandleGameObjectActivateCommand, "" },
{ "delete", SEC_ADMINISTRATOR, false, &HandleGameObjectDeleteCommand, "" },
{ "info", SEC_MODERATOR, false, &HandleGameObjectInfoCommand, "" },
{ "move", SEC_ADMINISTRATOR, false, &HandleGameObjectMoveCommand, "" },
@@ -63,7 +69,7 @@ public:
{ "add", SEC_ADMINISTRATOR, false, nullptr, "", gobjectAddCommandTable },
{ "set", SEC_ADMINISTRATOR, false, nullptr, "", gobjectSetCommandTable }
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "gobject", SEC_MODERATOR, false, nullptr, "", gobjectCommandTable }
};
diff --git a/src/server/scripts/Commands/cs_guild.cpp b/src/server/scripts/Commands/cs_guild.cpp
index 675bbc658b..89b867965a 100644
--- a/src/server/scripts/Commands/cs_guild.cpp
+++ b/src/server/scripts/Commands/cs_guild.cpp
@@ -29,14 +29,20 @@ EndScriptData */
#include "ObjectAccessor.h"
#include "ScriptMgr.h"
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class guild_commandscript : public CommandScript
{
public:
guild_commandscript() : CommandScript("guild_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> guildCommandTable =
+ static ChatCommandTable guildCommandTable =
{
{ "create", SEC_GAMEMASTER, true, &HandleGuildCreateCommand, "" },
{ "delete", SEC_GAMEMASTER, true, &HandleGuildDeleteCommand, "" },
@@ -45,7 +51,7 @@ public:
{ "rank", SEC_GAMEMASTER, true, &HandleGuildRankCommand, "" },
{ "info", SEC_GAMEMASTER, true, &HandleGuildInfoCommand, "" }
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "guild", SEC_GAMEMASTER, true, nullptr, "", guildCommandTable }
};
@@ -182,21 +188,15 @@ public:
return true;
}
- static bool HandleGuildRankCommand(ChatHandler* handler, char const* args)
+ static bool HandleGuildRankCommand(ChatHandler* handler, Optional<PlayerIdentifier> player, uint8 rank)
{
- char* nameStr;
- char* rankStr;
- handler->extractOptFirstArg((char*)args, &nameStr, &rankStr);
- if (!rankStr)
- return false;
+ if (!player)
+ player = PlayerIdentifier::FromTargetOrSelf(handler);
- Player* target;
- ObjectGuid targetGuid;
- std::string target_name;
- if (!handler->extractPlayerTarget(nameStr, &target, &targetGuid, &target_name))
+ if (!player)
return false;
- uint32 guildId = target ? target->GetGuildId() : Player::GetGuildIdFromStorage(targetGuid.GetCounter());
+ uint32 guildId = player->IsConnected() ? player->GetConnectedPlayer()->GetGuildId() : Player::GetGuildIdFromStorage(player->GetGUID().GetCounter());
if (!guildId)
return false;
@@ -204,8 +204,7 @@ public:
if (!targetGuild)
return false;
- uint8 newRank = uint8(atoi(rankStr));
- return targetGuild->ChangeMemberRank(targetGuid, newRank);
+ return targetGuild->ChangeMemberRank(player->GetGUID(), rank);
}
static bool HandleGuildInfoCommand(ChatHandler* handler, char const* args)
diff --git a/src/server/scripts/Commands/cs_honor.cpp b/src/server/scripts/Commands/cs_honor.cpp
index 6450b81e1e..0f63183964 100644
--- a/src/server/scripts/Commands/cs_honor.cpp
+++ b/src/server/scripts/Commands/cs_honor.cpp
@@ -28,26 +28,32 @@ EndScriptData */
#include "Player.h"
#include "ScriptMgr.h"
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class honor_commandscript : public CommandScript
{
public:
honor_commandscript() : CommandScript("honor_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> honorAddCommandTable =
+ static ChatCommandTable honorAddCommandTable =
{
{ "kill", SEC_GAMEMASTER, false, &HandleHonorAddKillCommand, "" },
{ "", SEC_GAMEMASTER, false, &HandleHonorAddCommand, "" }
};
- static std::vector<ChatCommand> honorCommandTable =
+ static ChatCommandTable honorCommandTable =
{
{ "add", SEC_GAMEMASTER, false, nullptr, "", honorAddCommandTable },
{ "update", SEC_GAMEMASTER, false, &HandleHonorUpdateCommand, "" }
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "honor", SEC_GAMEMASTER, false, nullptr, "", honorCommandTable }
};
diff --git a/src/server/scripts/Commands/cs_instance.cpp b/src/server/scripts/Commands/cs_instance.cpp
index 8823aa5564..2b4b026f51 100644
--- a/src/server/scripts/Commands/cs_instance.cpp
+++ b/src/server/scripts/Commands/cs_instance.cpp
@@ -31,14 +31,20 @@ EndScriptData */
#include "Player.h"
#include "ScriptMgr.h"
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class instance_commandscript : public CommandScript
{
public:
instance_commandscript() : CommandScript("instance_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> instanceCommandTable =
+ static ChatCommandTable instanceCommandTable =
{
{ "listbinds", SEC_MODERATOR, false, &HandleInstanceListBindsCommand, "" },
{ "unbind", SEC_GAMEMASTER, false, &HandleInstanceUnbindCommand, "" },
@@ -48,7 +54,7 @@ public:
{ "getbossstate", SEC_MODERATOR, true, &HandleInstanceGetBossStateCommand, "" }
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "instance", SEC_MODERATOR, true, nullptr, "", instanceCommandTable }
};
diff --git a/src/server/scripts/Commands/cs_learn.cpp b/src/server/scripts/Commands/cs_learn.cpp
index b585b3eb73..a76c88221e 100644
--- a/src/server/scripts/Commands/cs_learn.cpp
+++ b/src/server/scripts/Commands/cs_learn.cpp
@@ -32,14 +32,20 @@ EndScriptData */
#include "SpellInfo.h"
#include "SpellMgr.h"
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class learn_commandscript : public CommandScript, public PlayerCommand
{
public:
learn_commandscript() : CommandScript("learn_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> learnAllMyCommandTable =
+ static ChatCommandTable learnAllMyCommandTable =
{
{ "class", SEC_GAMEMASTER, false, &HandleLearnAllMyClassCommand, "" },
{ "pettalents", SEC_GAMEMASTER, false, &HandleLearnAllMyPetTalentsCommand, "" },
@@ -47,7 +53,7 @@ public:
{ "talents", SEC_GAMEMASTER, false, &HandleLearnAllMyTalentsCommand, "" }
};
- static std::vector<ChatCommand> learnAllCommandTable =
+ static ChatCommandTable learnAllCommandTable =
{
{ "my", SEC_GAMEMASTER, false, nullptr, "", learnAllMyCommandTable },
{ "gm", SEC_GAMEMASTER, false, &HandleLearnAllGMCommand, "" },
@@ -57,13 +63,13 @@ public:
{ "recipes", SEC_GAMEMASTER, false, &HandleLearnAllRecipesCommand, "" }
};
- static std::vector<ChatCommand> learnCommandTable =
+ static ChatCommandTable learnCommandTable =
{
{ "all", SEC_GAMEMASTER, false, nullptr, "", learnAllCommandTable },
{ "", SEC_GAMEMASTER, false, &HandleLearnCommand, "" }
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "learn", SEC_GAMEMASTER, false, nullptr, "", learnCommandTable },
{ "unlearn", SEC_GAMEMASTER, false, &HandleUnLearnCommand, "" }
diff --git a/src/server/scripts/Commands/cs_lfg.cpp b/src/server/scripts/Commands/cs_lfg.cpp
index 06114f61e1..7653c446eb 100644
--- a/src/server/scripts/Commands/cs_lfg.cpp
+++ b/src/server/scripts/Commands/cs_lfg.cpp
@@ -36,14 +36,20 @@ void GetPlayerInfo(ChatHandler* handler, Player* player)
lfg::GetRolesString(sLFGMgr->GetRoles(guid)).c_str(), sLFGMgr->GetComment(guid).c_str());
}
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class lfg_commandscript : public CommandScript
{
public:
lfg_commandscript() : CommandScript("lfg_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> lfgCommandTable =
+ static ChatCommandTable lfgCommandTable =
{
{ "player", SEC_MODERATOR, false, &HandleLfgPlayerInfoCommand, "" },
{ "group", SEC_MODERATOR, false, &HandleLfgGroupInfoCommand, "" },
@@ -52,7 +58,7 @@ public:
{ "options", SEC_GAMEMASTER, false, &HandleLfgOptionsCommand, "" },
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "lfg", SEC_GAMEMASTER, false, nullptr, "", lfgCommandTable },
};
diff --git a/src/server/scripts/Commands/cs_list.cpp b/src/server/scripts/Commands/cs_list.cpp
index 539ea79247..a8a1cef9f6 100644
--- a/src/server/scripts/Commands/cs_list.cpp
+++ b/src/server/scripts/Commands/cs_list.cpp
@@ -29,14 +29,20 @@ EndScriptData */
#include "ScriptMgr.h"
#include "SpellAuraEffects.h"
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class list_commandscript : public CommandScript
{
public:
list_commandscript() : CommandScript("list_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> listCommandTable =
+ static ChatCommandTable listCommandTable =
{
{ "creature", SEC_MODERATOR, true, &HandleListCreatureCommand, "" },
{ "item", SEC_MODERATOR, true, &HandleListItemCommand, "" },
@@ -44,7 +50,7 @@ public:
{ "gobject", SEC_MODERATOR, true, &HandleListObjectCommand, "" },
{ "auras", SEC_MODERATOR, false, &HandleListAurasCommand, "" }
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "list", SEC_MODERATOR, true, nullptr, "", listCommandTable }
};
diff --git a/src/server/scripts/Commands/cs_lookup.cpp b/src/server/scripts/Commands/cs_lookup.cpp
index e1734acf45..0417159c17 100644
--- a/src/server/scripts/Commands/cs_lookup.cpp
+++ b/src/server/scripts/Commands/cs_lookup.cpp
@@ -32,27 +32,33 @@ EndScriptData */
#include "ScriptMgr.h"
#include "SpellInfo.h"
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class lookup_commandscript : public CommandScript
{
public:
lookup_commandscript() : CommandScript("lookup_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> lookupPlayerCommandTable =
+ static ChatCommandTable lookupPlayerCommandTable =
{
{ "ip", SEC_GAMEMASTER, true, &HandleLookupPlayerIpCommand, "" },
{ "account", SEC_GAMEMASTER, true, &HandleLookupPlayerAccountCommand, "" },
{ "email", SEC_GAMEMASTER, true, &HandleLookupPlayerEmailCommand, "" }
};
- static std::vector<ChatCommand> lookupSpellCommandTable =
+ static ChatCommandTable lookupSpellCommandTable =
{
{ "id", SEC_MODERATOR, true, &HandleLookupSpellIdCommand, "" },
{ "", SEC_MODERATOR, true, &HandleLookupSpellCommand, "" }
};
- static std::vector<ChatCommand> lookupCommandTable =
+ static ChatCommandTable lookupCommandTable =
{
{ "area", SEC_MODERATOR, true, &HandleLookupAreaCommand, "" },
{ "creature", SEC_MODERATOR, true, &HandleLookupCreatureCommand, "" },
@@ -72,7 +78,7 @@ public:
{ "spell", SEC_MODERATOR, true, nullptr, "", lookupSpellCommandTable }
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "lookup", SEC_MODERATOR, true, nullptr, "", lookupCommandTable }
};
diff --git a/src/server/scripts/Commands/cs_message.cpp b/src/server/scripts/Commands/cs_message.cpp
index 86fbc896a2..ff1c00bb54 100644
--- a/src/server/scripts/Commands/cs_message.cpp
+++ b/src/server/scripts/Commands/cs_message.cpp
@@ -28,14 +28,20 @@ EndScriptData */
#include "Player.h"
#include "ScriptMgr.h"
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class message_commandscript : public CommandScript
{
public:
message_commandscript() : CommandScript("message_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "nameannounce", SEC_GAMEMASTER, true, &HandleNameAnnounceCommand, "" },
{ "gmnameannounce", SEC_GAMEMASTER, true, &HandleGMNameAnnounceCommand, "" },
diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp
index ff2efdf70e..2086024440 100644
--- a/src/server/scripts/Commands/cs_misc.cpp
+++ b/src/server/scripts/Commands/cs_misc.cpp
@@ -41,14 +41,20 @@
#include "TargetedMovementGenerator.h"
#include "WeatherMgr.h"
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class misc_commandscript : public CommandScript
{
public:
misc_commandscript() : CommandScript("misc_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> groupCommandTable =
+ static ChatCommandTable groupCommandTable =
{
{ "leader", SEC_GAMEMASTER, false, &HandleGroupLeaderCommand, "" },
{ "disband", SEC_GAMEMASTER, false, &HandleGroupDisbandCommand, "" },
@@ -56,25 +62,25 @@ public:
{ "join", SEC_GAMEMASTER, false, &HandleGroupJoinCommand, "" },
{ "list", SEC_GAMEMASTER, false, &HandleGroupListCommand, "" }
};
- static std::vector<ChatCommand> petCommandTable =
+ static ChatCommandTable petCommandTable =
{
{ "create", SEC_GAMEMASTER, false, &HandleCreatePetCommand, "" },
{ "learn", SEC_GAMEMASTER, false, &HandlePetLearnCommand, "" },
{ "unlearn", SEC_GAMEMASTER, false, &HandlePetUnlearnCommand, "" }
};
- static std::vector<ChatCommand> sendCommandTable =
+ static ChatCommandTable sendCommandTable =
{
{ "items", SEC_GAMEMASTER, true, &HandleSendItemsCommand, "" },
{ "mail", SEC_GAMEMASTER, true, &HandleSendMailCommand, "" },
{ "message", SEC_ADMINISTRATOR, true, &HandleSendMessageCommand, "" },
{ "money", SEC_GAMEMASTER, true, &HandleSendMoneyCommand, "" }
};
- static std::vector<ChatCommand> gearCommandTable =
+ static ChatCommandTable gearCommandTable =
{
{ "repair", SEC_GAMEMASTER, false, &HandleGearRepairCommand, "" },
{ "stats", SEC_PLAYER, false, &HandleGearStatsCommand, "" }
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "dev", SEC_ADMINISTRATOR, false, &HandleDevCommand, "" },
{ "gps", SEC_MODERATOR, false, &HandleGPSCommand, "" },
@@ -894,7 +900,7 @@ public:
static bool HandleCommandsCommand(ChatHandler* handler, char const* /*args*/)
{
- handler->ShowHelpForCommand(handler->getCommandTable(), "");
+ SendCommandHelpFor(*handler, "");
return true;
}
@@ -993,19 +999,12 @@ public:
return true;
}
- static bool HandleHelpCommand(ChatHandler* handler, char const* args)
+ static bool HandleHelpCommand(ChatHandler* handler, Tail cmd)
{
- char const* cmd = strtok((char*)args, " ");
- if (!cmd)
- {
- handler->ShowHelpForCommand(handler->getCommandTable(), "help");
- handler->ShowHelpForCommand(handler->getCommandTable(), "");
- }
- else
- {
- if (!handler->ShowHelpForCommand(handler->getCommandTable(), cmd))
- handler->SendSysMessage(LANG_NO_HELP_CMD);
- }
+ Acore::ChatCommands::SendCommandHelpFor(*handler, cmd);
+
+ if (cmd.empty())
+ Acore::ChatCommands::SendCommandHelpFor(*handler, "help");
return true;
}
@@ -2242,36 +2241,33 @@ public:
return true;
}
// mute player for some times
- static bool HandleMuteCommand(ChatHandler* handler, char const* args)
+ static bool HandleMuteCommand(ChatHandler* handler, Optional<PlayerIdentifier> player, uint32 notSpeakTime, Tail muteReason)
{
- char* nameStr;
- char* delayStr;
- handler->extractOptFirstArg((char*)args, &nameStr, &delayStr);
- if (!delayStr)
- return false;
+ std::string muteReasonStr{ muteReason };
- char const* muteReason = strtok(nullptr, "\r");
- std::string muteReasonStr = handler->GetAcoreString(LANG_NO_REASON);
- if (muteReason != nullptr)
- muteReasonStr = muteReason;
+ if (muteReason.empty())
+ muteReasonStr = handler->GetAcoreString(LANG_NO_REASON);
- Player* target;
- ObjectGuid targetGuid;
- std::string targetName;
- if (!handler->extractPlayerTarget(nameStr, &target, &targetGuid, &targetName))
+ if (!player)
+ player = PlayerIdentifier::FromTarget(handler);
+
+ if (!player)
+ {
+ handler->SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ handler->SetSentErrorMessage(true);
return false;
+ }
- uint32 accountId = target ? target->GetSession()->GetAccountId() : sObjectMgr->GetPlayerAccountIdByGUID(targetGuid.GetCounter());
+ Player* target = player->GetConnectedPlayer();
+ uint32 accountId = target ? target->GetSession()->GetAccountId() : sObjectMgr->GetPlayerAccountIdByGUID(player->GetGUID().GetCounter());
// find only player from same account if any
if (!target)
if (WorldSession* session = sWorld->FindSession(accountId))
target = session->GetPlayer();
- uint32 notSpeakTime = uint32(atoi(delayStr));
-
// must have strong lesser security level
- if (handler->HasLowerSecurity (target, targetGuid, true))
+ if (handler->HasLowerSecurity(target, player->GetGUID(), true))
return false;
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_MUTE_TIME);
@@ -2287,7 +2283,7 @@ public:
int64 muteTime = time(nullptr) + notSpeakTime * MINUTE;
target->GetSession()->m_muteTime = muteTime;
stmt->setInt64(0, muteTime);
- std::string nameLink = handler->playerLink(targetName);
+ std::string nameLink = handler->playerLink(player->GetName());
if (sWorld->getBoolConfig(CONFIG_SHOW_MUTE_IN_WORLD))
sWorld->SendWorldText(LANG_COMMAND_MUTEMESSAGE_WORLD, muteBy.c_str(), nameLink.c_str(), notSpeakTime, muteReasonStr.c_str());
@@ -2297,21 +2293,22 @@ public:
else
{
// Target is offline, mute will be in effect starting from the next login.
- int32 muteTime = -int32(notSpeakTime * MINUTE);
- stmt->setInt64(0, muteTime);
+ stmt->setInt32(0, -int32(notSpeakTime * MINUTE));
}
stmt->setString(1, muteReasonStr);
stmt->setString(2, muteBy);
stmt->setUInt32(3, accountId);
LoginDatabase.Execute(stmt);
+
stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ACCOUNT_MUTE);
stmt->setUInt32(0, accountId);
stmt->setUInt32(1, notSpeakTime);
stmt->setString(2, muteBy);
stmt->setString(3, muteReasonStr);
LoginDatabase.Execute(stmt);
- std::string nameLink = handler->playerLink(targetName);
+
+ std::string nameLink = handler->playerLink(player->GetName());
if (sWorld->getBoolConfig(CONFIG_SHOW_MUTE_IN_WORLD) && !target)
sWorld->SendWorldText(LANG_COMMAND_MUTEMESSAGE_WORLD, muteBy.c_str(), nameLink.c_str(), notSpeakTime, muteReasonStr.c_str());
@@ -2322,7 +2319,8 @@ public:
HashMapHolder<Player>::MapType const& m = ObjectAccessor::GetPlayers();
for (HashMapHolder<Player>::MapType::const_iterator itr = m.begin(); itr != m.end(); ++itr)
if (itr->second->GetSession()->GetSecurity())
- ChatHandler(itr->second->GetSession()).PSendSysMessage(target ? LANG_YOU_DISABLE_CHAT : LANG_COMMAND_DISABLE_CHAT_DELAYED, (handler->GetSession() ? handler->GetSession()->GetPlayerName().c_str() : handler->GetAcoreString(LANG_CONSOLE)), nameLink.c_str(), notSpeakTime, muteReasonStr.c_str());
+ ChatHandler(itr->second->GetSession()).PSendSysMessage(target ? LANG_YOU_DISABLE_CHAT : LANG_COMMAND_DISABLE_CHAT_DELAYED,
+ (handler->GetSession() ? handler->GetSession()->GetPlayerName().c_str() : handler->GetAcoreString(LANG_CONSOLE)), nameLink.c_str(), notSpeakTime, muteReasonStr.c_str());
}
return true;
diff --git a/src/server/scripts/Commands/cs_mmaps.cpp b/src/server/scripts/Commands/cs_mmaps.cpp
index 84ea4a4930..e89d800171 100644
--- a/src/server/scripts/Commands/cs_mmaps.cpp
+++ b/src/server/scripts/Commands/cs_mmaps.cpp
@@ -36,14 +36,20 @@
#include "ScriptMgr.h"
#include "TargetedMovementGenerator.h"
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class mmaps_commandscript : public CommandScript
{
public:
mmaps_commandscript() : CommandScript("mmaps_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> mmapCommandTable =
+ static ChatCommandTable mmapCommandTable =
{
{ "loadedtiles", SEC_ADMINISTRATOR, false, &HandleMmapLoadedTilesCommand, "" },
{ "loc", SEC_ADMINISTRATOR, false, &HandleMmapLocCommand, "" },
@@ -52,7 +58,7 @@ public:
{ "testarea", SEC_ADMINISTRATOR, false, &HandleMmapTestArea, "" },
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "mmap", SEC_ADMINISTRATOR, true, nullptr, "", mmapCommandTable },
};
diff --git a/src/server/scripts/Commands/cs_modify.cpp b/src/server/scripts/Commands/cs_modify.cpp
index aeda44699a..b035528280 100644
--- a/src/server/scripts/Commands/cs_modify.cpp
+++ b/src/server/scripts/Commands/cs_modify.cpp
@@ -32,14 +32,20 @@ EndScriptData */
#include "ScriptMgr.h"
#include "StringConvert.h"
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class modify_commandscript : public CommandScript
{
public:
modify_commandscript() : CommandScript("modify_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> modifyspeedCommandTable =
+ static ChatCommandTable modifyspeedCommandTable =
{
{ "fly", SEC_GAMEMASTER, false, &HandleModifyFlyCommand, "" },
{ "all", SEC_GAMEMASTER, false, &HandleModifyASpeedCommand, "" },
@@ -49,7 +55,7 @@ public:
{ "", SEC_GAMEMASTER, false, &HandleModifyASpeedCommand, "" }
};
- static std::vector<ChatCommand> modifyCommandTable =
+ static ChatCommandTable modifyCommandTable =
{
{ "hp", SEC_GAMEMASTER, false, &HandleModifyHPCommand, "" },
{ "mana", SEC_GAMEMASTER, false, &HandleModifyManaCommand, "" },
@@ -60,7 +66,7 @@ public:
{ "scale", SEC_GAMEMASTER, false, &HandleModifyScaleCommand, "" },
{ "bit", SEC_GAMEMASTER, false, &HandleModifyBitCommand, "" },
{ "faction", SEC_ADMINISTRATOR, false, &HandleModifyFactionCommand, "" },
- { "spell", SEC_GAMEMASTER, false, &HandleModifySpellCommand, "" },
+ { "spell", SEC_CONSOLE, false, &HandleModifySpellCommand, "" },
{ "talentpoints", SEC_GAMEMASTER, false, &HandleModifyTalentCommand, "" },
{ "mount", SEC_GAMEMASTER, false, &HandleModifyMountCommand, "" },
{ "honor", SEC_GAMEMASTER, false, &HandleModifyHonorCommand, "" },
@@ -73,13 +79,13 @@ public:
{ "speed", SEC_GAMEMASTER, false, nullptr, "", modifyspeedCommandTable }
};
- static std::vector<ChatCommand> morphCommandTable =
+ static ChatCommandTable morphCommandTable =
{
{ "reset", SEC_MODERATOR, false, &HandleMorphResetCommand, "" },
{ "target", SEC_MODERATOR, false, &HandleMorphTargetCommand, "" }
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "morph", SEC_MODERATOR, false, nullptr, "", morphCommandTable },
{ "modify", SEC_GAMEMASTER, false, nullptr, "", modifyCommandTable }
diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp
index 316064707e..bacc8ab711 100644
--- a/src/server/scripts/Commands/cs_npc.cpp
+++ b/src/server/scripts/Commands/cs_npc.cpp
@@ -34,6 +34,12 @@ EndScriptData */
#include "Transport.h"
#include <string>
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
struct NpcFlagText
{
uint32 flag;
@@ -136,9 +142,9 @@ class npc_commandscript : public CommandScript
public:
npc_commandscript() : CommandScript("npc_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> npcAddCommandTable =
+ static ChatCommandTable npcAddCommandTable =
{
{ "formation", SEC_ADMINISTRATOR, false, &HandleNpcAddFormationCommand, "" },
{ "item", SEC_ADMINISTRATOR, false, &HandleNpcAddVendorItemCommand, "" },
@@ -149,25 +155,25 @@ public:
//}
{ "", SEC_ADMINISTRATOR, false, &HandleNpcAddCommand, "" }
};
- static std::vector<ChatCommand> npcDeleteCommandTable =
+ static ChatCommandTable npcDeleteCommandTable =
{
{ "item", SEC_ADMINISTRATOR, false, &HandleNpcDeleteVendorItemCommand, "" },
{ "", SEC_ADMINISTRATOR, false, &HandleNpcDeleteCommand, "" }
};
- static std::vector<ChatCommand> npcFollowCommandTable =
+ static ChatCommandTable npcFollowCommandTable =
{
{ "stop", SEC_GAMEMASTER, false, &HandleNpcUnFollowCommand, "" },
{ "", SEC_GAMEMASTER, false, &HandleNpcFollowCommand, "" }
};
- static std::vector<ChatCommand> npcFactionCommandTable =
+ static ChatCommandTable npcFactionCommandTable =
{
{ "permanent", SEC_ADMINISTRATOR, false, &HandleNpcSetFactionIdCommand, "" },
{ "temp", SEC_ADMINISTRATOR, false, &HandleNpcSetFactionTempIdCommand, "" },
{ "original", SEC_ADMINISTRATOR, false, &HandleNpcSetOriginalFaction, "" }
};
- static std::vector<ChatCommand> npcSetCommandTable =
+ static ChatCommandTable npcSetCommandTable =
{
{ "allowmove", SEC_ADMINISTRATOR, false, &HandleNpcSetAllowMovementCommand, "" },
{ "entry", SEC_ADMINISTRATOR, false, &HandleNpcSetEntryCommand, "" },
@@ -186,7 +192,7 @@ public:
{ "subname", SEC_ADMINISTRATOR, false, &HandleNpcSetSubNameCommand, "" }
//}
};
- static std::vector<ChatCommand> npcCommandTable =
+ static ChatCommandTable npcCommandTable =
{
{ "info", SEC_MODERATOR, false, &HandleNpcInfoCommand, "" },
{ "near", SEC_GAMEMASTER, false, &HandleNpcNearCommand, "" },
@@ -202,7 +208,7 @@ public:
{ "follow", SEC_GAMEMASTER, false, nullptr, "", npcFollowCommandTable },
{ "set", SEC_ADMINISTRATOR, false, nullptr, "", npcSetCommandTable }
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "npc", SEC_MODERATOR, false, nullptr, "", npcCommandTable }
};
@@ -705,7 +711,7 @@ public:
creature->AI()->SetData(data_1, data_2);
std::string AIorScript = creature->GetAIName() != "" ? "AI type: " + creature->GetAIName() : (creature->GetScriptName() != "" ? "Script Name: " + creature->GetScriptName() : "No AI or Script Name Set");
- handler->PSendSysMessage(LANG_NPC_SETDATA, creature->GetGUID(), creature->GetEntry(), creature->GetName().c_str(), data_1, data_2, AIorScript.c_str());
+ handler->PSendSysMessage(LANG_NPC_SETDATA, creature->GetGUID().GetCounter(), creature->GetEntry(), creature->GetName().c_str(), data_1, data_2, AIorScript.c_str());
return true;
}
diff --git a/src/server/scripts/Commands/cs_player.cpp b/src/server/scripts/Commands/cs_player.cpp
index a5f2ce2d8e..46c82a927d 100644
--- a/src/server/scripts/Commands/cs_player.cpp
+++ b/src/server/scripts/Commands/cs_player.cpp
@@ -21,20 +21,26 @@
#include "PlayerCommand.h"
#include "ScriptMgr.h"
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class player_commandscript : public CommandScript, public PlayerCommand
{
public:
player_commandscript() : CommandScript("player_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> playerCommandTable =
+ static ChatCommandTable playerCommandTable =
{
{ "learn", SEC_GAMEMASTER, true, &HandlePlayerLearnCommand, "" },
{ "unlearn", SEC_GAMEMASTER, true, &HandlePlayerUnLearnCommand, "" }
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "player", SEC_GAMEMASTER, true, nullptr, "", playerCommandTable }
};
diff --git a/src/server/scripts/Commands/cs_quest.cpp b/src/server/scripts/Commands/cs_quest.cpp
index baa8d99680..bfe9a1c22d 100644
--- a/src/server/scripts/Commands/cs_quest.cpp
+++ b/src/server/scripts/Commands/cs_quest.cpp
@@ -28,21 +28,27 @@ EndScriptData */
#include "ReputationMgr.h"
#include "ScriptMgr.h"
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class quest_commandscript : public CommandScript
{
public:
quest_commandscript() : CommandScript("quest_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> questCommandTable =
+ static ChatCommandTable questCommandTable =
{
{ "add", SEC_GAMEMASTER, false, &HandleQuestAdd, "" },
{ "complete", SEC_GAMEMASTER, false, &HandleQuestComplete, "" },
{ "remove", SEC_GAMEMASTER, false, &HandleQuestRemove, "" },
{ "reward", SEC_GAMEMASTER, false, &HandleQuestReward, "" },
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "quest", SEC_GAMEMASTER, false, nullptr, "", questCommandTable },
};
diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp
index 603d79a902..49639bdd10 100644
--- a/src/server/scripts/Commands/cs_reload.cpp
+++ b/src/server/scripts/Commands/cs_reload.cpp
@@ -44,14 +44,20 @@ EndScriptData */
#include "StringConvert.h"
#include "Tokenize.h"
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class reload_commandscript : public CommandScript
{
public:
reload_commandscript() : CommandScript("reload_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> reloadAllCommandTable =
+ static ChatCommandTable reloadAllCommandTable =
{
{ "achievement", SEC_ADMINISTRATOR, true, &HandleReloadAllAchievementCommand, "" },
{ "area", SEC_ADMINISTRATOR, true, &HandleReloadAllAreaCommand, "" },
@@ -65,7 +71,7 @@ public:
{ "spell", SEC_ADMINISTRATOR, true, &HandleReloadAllSpellCommand, "" },
{ "", SEC_ADMINISTRATOR, true, &HandleReloadAllCommand, "" }
};
- static std::vector<ChatCommand> reloadCommandTable =
+ static ChatCommandTable reloadCommandTable =
{
{ "auctions", SEC_ADMINISTRATOR, true, &HandleReloadAuctionsCommand, "" },
{ "dungeon_access_template", SEC_ADMINISTRATOR, true, &HandleReloadDungeonAccessCommand, "" },
@@ -85,7 +91,7 @@ public:
{ "config", SEC_ADMINISTRATOR, true, &HandleReloadConfigCommand, "" },
{ "creature_text", SEC_ADMINISTRATOR, true, &HandleReloadCreatureText, "" },
{ "creature_questender", SEC_ADMINISTRATOR, true, &HandleReloadCreatureQuestEnderCommand, "" },
- { "creature_linked_respawn", SEC_GAMEMASTER, true, &HandleReloadLinkedRespawnCommand, "" },
+ { "creature_linked_respawn", SEC_ADMINISTRATOR, true, &HandleReloadLinkedRespawnCommand, "" },
{ "creature_loot_template", SEC_ADMINISTRATOR, true, &HandleReloadLootTemplatesCreatureCommand, "" },
{ "creature_onkill_reputation", SEC_ADMINISTRATOR, true, &HandleReloadOnKillReputationCommand, "" },
{ "creature_queststarter", SEC_ADMINISTRATOR, true, &HandleReloadCreatureQuestStarterCommand, "" },
@@ -161,7 +167,7 @@ public:
{ "vehicle_accessory", SEC_ADMINISTRATOR, true, &HandleReloadVehicleAccessoryCommand, "" },
{ "vehicle_template_accessory", SEC_ADMINISTRATOR, true, &HandleReloadVehicleTemplateAccessoryCommand, "" }
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "reload", SEC_ADMINISTRATOR, true, nullptr, "", reloadCommandTable }
};
@@ -405,9 +411,13 @@ public:
static bool HandleReloadCommandCommand(ChatHandler* handler, const char* /*args*/)
{
- handler->SetLoadCommandTable(true);
- handler->SendGlobalGMSysMessage("DB table `command` will be reloaded at next chat command use.");
- return true;
+ LOG_INFO("server.loading", "Reloading .command information...");
+ Acore::ChatCommands::LoadCommandMap();
+ handler->SendGlobalGMSysMessage("DB table `command` reloaded.");
+
+ // do not log this invocation, otherwise we might crash (the command table we used to get here is no longer valid!)
+ handler->SetSentErrorMessage(true);
+ return false;
}
static bool HandleReloadOnKillReputationCommand(ChatHandler* handler, const char* /*args*/)
diff --git a/src/server/scripts/Commands/cs_reset.cpp b/src/server/scripts/Commands/cs_reset.cpp
index b22ca5ab33..f1c1e86076 100644
--- a/src/server/scripts/Commands/cs_reset.cpp
+++ b/src/server/scripts/Commands/cs_reset.cpp
@@ -30,14 +30,20 @@ EndScriptData */
#include "Player.h"
#include "ScriptMgr.h"
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class reset_commandscript : public CommandScript
{
public:
reset_commandscript() : CommandScript("reset_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> resetCommandTable =
+ static ChatCommandTable resetCommandTable =
{
{ "achievements", SEC_CONSOLE, true, &HandleResetAchievementsCommand, "" },
{ "honor", SEC_ADMINISTRATOR, true, &HandleResetHonorCommand, "" },
@@ -47,7 +53,7 @@ public:
{ "talents", SEC_ADMINISTRATOR, true, &HandleResetTalentsCommand, "" },
{ "all", SEC_CONSOLE, true, &HandleResetAllCommand, "" }
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "reset", SEC_ADMINISTRATOR, true, nullptr, "", resetCommandTable }
};
diff --git a/src/server/scripts/Commands/cs_server.cpp b/src/server/scripts/Commands/cs_server.cpp
index 55f6ca2c02..9dae990b3a 100644
--- a/src/server/scripts/Commands/cs_server.cpp
+++ b/src/server/scripts/Commands/cs_server.cpp
@@ -41,38 +41,44 @@ EndScriptData */
#include <openssl/opensslv.h>
#include <numeric>
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class server_commandscript : public CommandScript
{
public:
server_commandscript() : CommandScript("server_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> serverIdleRestartCommandTable =
+ static ChatCommandTable serverIdleRestartCommandTable =
{
- { "cancel", SEC_ADMINISTRATOR, true, &HandleServerShutDownCancelCommand, "" },
- { "", SEC_ADMINISTRATOR, true, &HandleServerIdleRestartCommand, "" }
+ { "cancel", SEC_ADMINISTRATOR, true, &HandleServerShutDownCancelCommand, "" },
+ { "", SEC_CONSOLE, true, &HandleServerIdleRestartCommand, "" }
};
- static std::vector<ChatCommand> serverIdleShutdownCommandTable =
+ static ChatCommandTable serverIdleShutdownCommandTable =
{
- { "cancel", SEC_ADMINISTRATOR, true, &HandleServerShutDownCancelCommand, "" },
- { "", SEC_ADMINISTRATOR, true, &HandleServerIdleShutDownCommand, "" }
+ { "cancel", SEC_ADMINISTRATOR, true, &HandleServerShutDownCancelCommand, "" },
+ { "", SEC_CONSOLE, true, &HandleServerIdleShutDownCommand, "" }
};
- static std::vector<ChatCommand> serverRestartCommandTable =
+ static ChatCommandTable serverRestartCommandTable =
{
- { "cancel", SEC_ADMINISTRATOR, true, &HandleServerShutDownCancelCommand, "" },
- { "", SEC_ADMINISTRATOR, true, &HandleServerRestartCommand, "" }
+ { "cancel", SEC_ADMINISTRATOR, true, &HandleServerShutDownCancelCommand, "" },
+ { "", SEC_ADMINISTRATOR, true, &HandleServerRestartCommand, "" }
};
- static std::vector<ChatCommand> serverShutdownCommandTable =
+ static ChatCommandTable serverShutdownCommandTable =
{
- { "cancel", SEC_ADMINISTRATOR, true, &HandleServerShutDownCancelCommand, "" },
- { "", SEC_ADMINISTRATOR, true, &HandleServerShutDownCommand, "" }
+ { "cancel", SEC_ADMINISTRATOR, true, &HandleServerShutDownCancelCommand, "" },
+ { "", SEC_ADMINISTRATOR, true, &HandleServerShutDownCommand, "" }
};
- static std::vector<ChatCommand> serverSetCommandTable =
+ static ChatCommandTable serverSetCommandTable =
{
{ "difftime", SEC_CONSOLE, true, &HandleServerSetDiffTimeCommand, "" },
{ "loglevel", SEC_CONSOLE, true, &HandleServerSetLogLevelCommand, "" },
@@ -80,7 +86,7 @@ public:
{ "closed", SEC_CONSOLE, true, &HandleServerSetClosedCommand, "" }
};
- static std::vector<ChatCommand> serverCommandTable =
+ static ChatCommandTable serverCommandTable =
{
{ "corpses", SEC_GAMEMASTER, true, &HandleServerCorpsesCommand, "" },
{ "debug", SEC_ADMINISTRATOR, true, &HandleServerDebugCommand, "" },
@@ -94,7 +100,7 @@ public:
{ "set", SEC_ADMINISTRATOR, true, nullptr, "", serverSetCommandTable }
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "server", SEC_PLAYER, true, nullptr, "", serverCommandTable }
};
diff --git a/src/server/scripts/Commands/cs_spectator.cpp b/src/server/scripts/Commands/cs_spectator.cpp
index 9b63f29283..b5d71f9fbb 100644
--- a/src/server/scripts/Commands/cs_spectator.cpp
+++ b/src/server/scripts/Commands/cs_spectator.cpp
@@ -23,25 +23,31 @@
#include "ScriptMgr.h"
#include "World.h"
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class spectator_commandscript : public CommandScript
{
public:
spectator_commandscript() : CommandScript("spectator_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> spectatorCommandTable =
+ static ChatCommandTable spectatorCommandTable =
{
- { "version", SEC_CONSOLE, false, &HandleSpectatorVersionCommand, "" },
- { "reset", SEC_CONSOLE, false, &HandleSpectatorResetCommand, "" },
- { "spectate", SEC_CONSOLE, false, &HandleSpectatorSpectateCommand, "" },
- { "watch", SEC_CONSOLE, false, &HandleSpectatorWatchCommand, "" },
- { "leave", SEC_CONSOLE, false, &HandleSpectatorLeaveCommand, "" },
- { "", SEC_CONSOLE, false, &HandleSpectatorCommand, "" }
+ { "version", SEC_PLAYER, false, &HandleSpectatorVersionCommand, "" },
+ { "reset", SEC_PLAYER, false, &HandleSpectatorResetCommand, "" },
+ { "spectate", SEC_PLAYER, false, &HandleSpectatorSpectateCommand, "" },
+ { "watch", SEC_PLAYER, false, &HandleSpectatorWatchCommand, "" },
+ { "leave", SEC_PLAYER, false, &HandleSpectatorLeaveCommand, "" },
+ { "", SEC_PLAYER, false, &HandleSpectatorCommand, "" }
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
- { "spect", SEC_CONSOLE, false, nullptr, "", spectatorCommandTable }
+ { "spect", SEC_PLAYER, false, nullptr, "", spectatorCommandTable }
};
return commandTable;
}
diff --git a/src/server/scripts/Commands/cs_tele.cpp b/src/server/scripts/Commands/cs_tele.cpp
index aafc767547..7866f955cf 100644
--- a/src/server/scripts/Commands/cs_tele.cpp
+++ b/src/server/scripts/Commands/cs_tele.cpp
@@ -30,14 +30,20 @@ EndScriptData */
#include "Player.h"
#include "ScriptMgr.h"
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class tele_commandscript : public CommandScript
{
public:
tele_commandscript() : CommandScript("tele_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> teleCommandTable =
+ static ChatCommandTable teleCommandTable =
{
{ "add", SEC_ADMINISTRATOR, false, &HandleTeleAddCommand, "" },
{ "del", SEC_ADMINISTRATOR, true, &HandleTeleDelCommand, "" },
@@ -45,7 +51,7 @@ public:
{ "group", SEC_GAMEMASTER, false, &HandleTeleGroupCommand, "" },
{ "", SEC_MODERATOR, false, &HandleTeleCommand, "" }
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "teleport", SEC_MODERATOR, false, nullptr, "", teleCommandTable }
};
@@ -92,82 +98,37 @@ public:
return true;
}
- static bool HandleTeleDelCommand(ChatHandler* handler, const char* args)
+ static bool HandleTeleDelCommand(ChatHandler* handler, GameTele const* tele)
{
- if (!*args)
- return false;
-
- // id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r
- GameTele const* tele = handler->extractGameTeleFromLink((char*)args);
if (!tele)
{
handler->SendSysMessage(LANG_COMMAND_TELE_NOTFOUND);
handler->SetSentErrorMessage(true);
return false;
}
+
std::string name = tele->name;
sObjectMgr->DeleteGameTele(name);
handler->SendSysMessage(LANG_COMMAND_TP_DELETED);
return true;
}
- // teleport player to given game_tele.entry
- static bool HandleTeleNameCommand(ChatHandler* handler, const char* args)
+ static bool DoNameTeleport(ChatHandler* handler, PlayerIdentifier player, uint32 mapId, Position const& pos, std::string const& locationName)
{
- char* nameStr;
- char* teleStr;
- handler->extractOptFirstArg((char*)args, &nameStr, &teleStr);
- if (!teleStr)
- return false;
-
- Player* target;
- ObjectGuid target_guid;
- std::string target_name;
- if (!handler->extractPlayerTarget(nameStr, &target, &target_guid, &target_name))
- return false;
-
- if (strcmp(teleStr, "$home") == 0) // References target's homebind
+ if (!MapMgr::IsValidMapCoord(mapId, pos) || sObjectMgr->IsTransportMap(mapId))
{
- if (target)
- target->TeleportTo(target->m_homebindMapId, target->m_homebindX, target->m_homebindY, target->m_homebindZ, target->GetOrientation());
- else
- {
- CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_HOMEBIND);
- stmt->setUInt32(0, target_guid.GetCounter());
- PreparedQueryResult resultDB = CharacterDatabase.Query(stmt);
-
- if (resultDB)
- {
- Field* fieldsDB = resultDB->Fetch();
- uint32 mapId = fieldsDB[0].GetUInt16();
- uint32 zoneId = fieldsDB[1].GetUInt16();
- float posX = fieldsDB[2].GetFloat();
- float posY = fieldsDB[3].GetFloat();
- float posZ = fieldsDB[4].GetFloat();
-
- Player::SavePositionInDB(mapId, posX, posY, posZ, 0, zoneId, target_guid);
- }
- }
-
- return true;
- }
-
- // id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r
- GameTele const* tele = handler->extractGameTeleFromLink(teleStr);
- if (!tele)
- {
- handler->SendSysMessage(LANG_COMMAND_TELE_NOTFOUND);
+ handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, pos.GetPositionX(), pos.GetPositionY(), mapId);
handler->SetSentErrorMessage(true);
return false;
}
- if (target)
+ if (Player* target = player.GetConnectedPlayer())
{
// check online security
- if (handler->HasLowerSecurity(target))
+ if (handler->HasLowerSecurity(target, ObjectGuid::Empty))
return false;
- std::string chrNameLink = handler->playerLink(target_name);
+ std::string chrNameLink = handler->playerLink(target->GetName());
if (target->IsBeingTeleported())
{
@@ -176,9 +137,9 @@ public:
return false;
}
- handler->PSendSysMessage(LANG_TELEPORTING_TO, chrNameLink.c_str(), "", tele->name.c_str());
+ handler->PSendSysMessage(LANG_TELEPORTING_TO, chrNameLink.c_str(), "", locationName.c_str());
if (handler->needReportToTarget(target))
- (ChatHandler(target->GetSession())).PSendSysMessage(LANG_TELEPORTED_TO_BY, handler->GetNameLink().c_str());
+ ChatHandler(target->GetSession()).PSendSysMessage(LANG_TELEPORTED_TO_BY, handler->GetNameLink().c_str());
// stop flight if need
if (target->IsInFlight())
@@ -186,33 +147,73 @@ public:
target->GetMotionMaster()->MovementExpired();
target->CleanupAfterTaxiFlight();
}
- // save only in non-flight case
- else
+ else // save only in non-flight case
target->SaveRecallPosition();
- target->TeleportTo(tele->mapId, tele->position_x, tele->position_y, tele->position_z, tele->orientation);
+ target->TeleportTo({ mapId, pos });
}
else
{
// check offline security
- if (handler->HasLowerSecurity(nullptr, target_guid))
+ if (handler->HasLowerSecurity(nullptr, player.GetGUID()))
return false;
- std::string nameLink = handler->playerLink(target_name);
+ std::string nameLink = handler->playerLink(player.GetName());
+
+ handler->PSendSysMessage(LANG_TELEPORTING_TO, nameLink.c_str(), handler->GetAcoreString(LANG_OFFLINE), locationName.c_str());
- handler->PSendSysMessage(LANG_TELEPORTING_TO, nameLink.c_str(), handler->GetAcoreString(LANG_OFFLINE), tele->name.c_str());
- Player::SavePositionInDB(tele->mapId, tele->position_x, tele->position_y, tele->position_z, tele->orientation,
- sMapMgr->GetZoneId(PHASEMASK_NORMAL, tele->mapId, tele->position_x, tele->position_y, tele->position_z), target_guid);
+ Player::SavePositionInDB({mapId, pos}, sMapMgr->GetZoneId(PHASEMASK_NORMAL, {mapId, pos}), player.GetGUID(), nullptr);
}
return true;
}
+ // teleport player to given game_tele.entry
+ static bool HandleTeleNameCommand(ChatHandler* handler, Optional<PlayerIdentifier> player, Variant<GameTele const*, EXACT_SEQUENCE("$home")> where)
+ {
+ if (!player)
+ player = PlayerIdentifier::FromTargetOrSelf(handler);
+
+ if (!player)
+ return false;
+
+ if (where.index() == 1) // References target's homebind
+ {
+ if (Player* target = player->GetConnectedPlayer())
+ target->TeleportTo(target->m_homebindMapId, target->m_homebindX, target->m_homebindY, target->m_homebindZ, target->GetOrientation());
+ else
+ {
+ CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_HOMEBIND);
+ stmt->setUInt32(0, player->GetGUID().GetCounter());
+ PreparedQueryResult resultDB = CharacterDatabase.Query(stmt);
+
+ if (resultDB)
+ {
+ Field* fieldsDB = resultDB->Fetch();
+ WorldLocation loc(fieldsDB[0].GetUInt16(), fieldsDB[2].GetFloat(), fieldsDB[3].GetFloat(), fieldsDB[4].GetFloat(), 0.0f);
+ uint32 zoneId = fieldsDB[1].GetUInt16();
+
+ Player::SavePositionInDB(loc, zoneId, player->GetGUID(), nullptr);
+ }
+ }
+
+ return true;
+ }
+
+ // id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r
+ GameTele const* tele = where.get<GameTele const*>();
+ return DoNameTeleport(handler, *player, tele->mapId, { tele->position_x, tele->position_y, tele->position_z, tele->orientation }, tele->name);
+ }
+
//Teleport group to given game_tele.entry
- static bool HandleTeleGroupCommand(ChatHandler* handler, const char* args)
+ static bool HandleTeleGroupCommand(ChatHandler* handler, GameTele const* tele)
{
- if (!*args)
+ if (!tele)
+ {
+ handler->SendSysMessage(LANG_COMMAND_TELE_NOTFOUND);
+ handler->SetSentErrorMessage(true);
return false;
+ }
Player* target = handler->getSelectedPlayer();
if (!target)
@@ -226,15 +227,6 @@ public:
if (handler->HasLowerSecurity(target))
return false;
- // id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r
- GameTele const* tele = handler->extractGameTeleFromLink((char*)args);
- if (!tele)
- {
- handler->SendSysMessage(LANG_COMMAND_TELE_NOTFOUND);
- handler->SetSentErrorMessage(true);
- return false;
- }
-
MapEntry const* map = sMapStore.LookupEntry(tele->mapId);
if (!map || map->IsBattlegroundOrArena())
{
@@ -292,16 +284,17 @@ public:
return true;
}
- static bool HandleTeleCommand(ChatHandler* handler, const char* args)
+ static bool HandleTeleCommand(ChatHandler* handler, GameTele const* tele)
{
- if (!*args)
+ if (!tele)
+ {
+ handler->SendSysMessage(LANG_COMMAND_TELE_NOTFOUND);
+ handler->SetSentErrorMessage(true);
return false;
+ }
Player* me = handler->GetSession()->GetPlayer();
- // id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r
- GameTele const* tele = handler->extractGameTeleFromLink((char*)args);
-
if (!tele)
{
handler->SendSysMessage(LANG_COMMAND_TELE_NOTFOUND);
diff --git a/src/server/scripts/Commands/cs_ticket.cpp b/src/server/scripts/Commands/cs_ticket.cpp
index fc91ecca71..cc4d2af9fa 100644
--- a/src/server/scripts/Commands/cs_ticket.cpp
+++ b/src/server/scripts/Commands/cs_ticket.cpp
@@ -32,19 +32,25 @@ EndScriptData */
#include "ScriptMgr.h"
#include "TicketMgr.h"
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class ticket_commandscript : public CommandScript
{
public:
ticket_commandscript() : CommandScript("ticket_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> ticketResponseCommandTable =
+ static ChatCommandTable ticketResponseCommandTable =
{
{ "append", SEC_GAMEMASTER, true, &HandleGMTicketResponseAppendCommand, "" },
{ "appendln", SEC_GAMEMASTER, true, &HandleGMTicketResponseAppendLnCommand, "" }
};
- static std::vector<ChatCommand> ticketCommandTable =
+ static ChatCommandTable ticketCommandTable =
{
{ "assign", SEC_GAMEMASTER, true, &HandleGMTicketAssignToCommand, "" },
{ "close", SEC_GAMEMASTER, true, &HandleGMTicketCloseByIdCommand, "" },
@@ -63,7 +69,7 @@ public:
{ "viewid", SEC_GAMEMASTER, true, &HandleGMTicketGetByIdCommand, "" },
{ "viewname", SEC_GAMEMASTER, true, &HandleGMTicketGetByNameCommand, "" }
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "ticket", SEC_GAMEMASTER, false, nullptr, "", ticketCommandTable }
};
diff --git a/src/server/scripts/Commands/cs_titles.cpp b/src/server/scripts/Commands/cs_titles.cpp
index 368910bf65..55f497948b 100644
--- a/src/server/scripts/Commands/cs_titles.cpp
+++ b/src/server/scripts/Commands/cs_titles.cpp
@@ -28,25 +28,31 @@ EndScriptData */
#include "Player.h"
#include "ScriptMgr.h"
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class titles_commandscript : public CommandScript
{
public:
titles_commandscript() : CommandScript("titles_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> titlesSetCommandTable =
+ static ChatCommandTable titlesSetCommandTable =
{
{ "mask", SEC_GAMEMASTER, false, &HandleTitlesSetMaskCommand, "" }
};
- static std::vector<ChatCommand> titlesCommandTable =
+ static ChatCommandTable titlesCommandTable =
{
{ "add", SEC_GAMEMASTER, false, &HandleTitlesAddCommand, "" },
{ "current", SEC_GAMEMASTER, false, &HandleTitlesCurrentCommand, "" },
{ "remove", SEC_GAMEMASTER, false, &HandleTitlesRemoveCommand, "" },
{ "set", SEC_GAMEMASTER, false, nullptr, "", titlesSetCommandTable }
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "titles", SEC_GAMEMASTER, false, nullptr, "", titlesCommandTable }
};
diff --git a/src/server/scripts/Commands/cs_wp.cpp b/src/server/scripts/Commands/cs_wp.cpp
index 9b215ee93a..0fe661f3c2 100644
--- a/src/server/scripts/Commands/cs_wp.cpp
+++ b/src/server/scripts/Commands/cs_wp.cpp
@@ -29,14 +29,20 @@ EndScriptData */
#include "ScriptMgr.h"
#include "WaypointMgr.h"
+#if AC_COMPILER == AC_COMPILER_GNU
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+using namespace Acore::ChatCommands;
+
class wp_commandscript : public CommandScript
{
public:
wp_commandscript() : CommandScript("wp_commandscript") { }
- std::vector<ChatCommand> GetCommands() const override
+ ChatCommandTable GetCommands() const override
{
- static std::vector<ChatCommand> wpCommandTable =
+ static ChatCommandTable wpCommandTable =
{
{ "add", SEC_ADMINISTRATOR, false, &HandleWpAddCommand, "" },
{ "event", SEC_ADMINISTRATOR, false, &HandleWpEventCommand, "" },
@@ -46,7 +52,7 @@ public:
{ "reload", SEC_ADMINISTRATOR, false, &HandleWpReloadCommand, "" },
{ "show", SEC_ADMINISTRATOR, false, &HandleWpShowCommand, "" }
};
- static std::vector<ChatCommand> commandTable =
+ static ChatCommandTable commandTable =
{
{ "wp", SEC_ADMINISTRATOR, false, nullptr, "", wpCommandTable }
};
diff --git a/src/server/shared/DataStores/DBCStructure.h b/src/server/shared/DataStores/DBCStructure.h
index 4f45dd5d2a..6db18d0343 100644
--- a/src/server/shared/DataStores/DBCStructure.h
+++ b/src/server/shared/DataStores/DBCStructure.h
@@ -41,7 +41,7 @@ struct AchievementEntry
int32 requiredFaction; // 1 -1=all, 0=horde, 1=alliance
int32 mapID; // 2 -1=none
//uint32 parentAchievement; // 3 its Achievement parent (can`t start while parent uncomplete, use its Criteria if don`t have own, use its progress on begin)
- char* name[16]; // 4-19
+ std::array<char const*, 16> name; // 4-19
//uint32 name_flags; // 20
//char *description[16]; // 21-36
//uint32 desc_flags; // 37
@@ -58,8 +58,8 @@ struct AchievementEntry
struct AchievementCategoryEntry
{
- int32 ID; // 0
- int32 parentCategory; // 1 -1 for main category
+ int32 ID; // 0
+ int32 parentCategory; // 1 -1 for main category
//char *name[16]; // 2-17
//uint32 name_flags; // 18
//uint32 sortOrder; // 19
@@ -258,7 +258,7 @@ struct AchievementCriteriaEntry
struct
{
uint32 teamtype; // 3 {2, 3, 5}
- uint32 PersonalRating; // 4
+ uint32 PersonalRating; // 4
} highest_personal_rating;
// ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL = 40
@@ -503,7 +503,7 @@ struct AchievementCriteriaEntry
uint32 additionalRequirement_value;
} additionalRequirements[MAX_CRITERIA_REQUIREMENTS];
- //char* name[16]; // 9-24
+ //char const* name[16]; // 9-24
//uint32 name_flags; // 25
uint32 flags; // 26
uint32 timedType; // 27
@@ -523,7 +523,7 @@ struct AreaTableEntry
uint32 flags; // 4, unknown value but 312 for all cities
// 5-9 unused
int32 area_level; // 10
- char* area_name[16]; // 11-26
+ char const* area_name[16]; // 11-26
// 27, string flags, unused
uint32 team; // 28
uint32 LiquidTypeOverride[4]; // 29-32 liquid override by type
@@ -553,20 +553,20 @@ struct AreaGroupEntry
struct AreaPOIEntry
{
- uint32 id; //0
- uint32 icon[11]; //1-11
- float x; //12
- float y; //13
- float z; //14
- uint32 mapId; //15
- //uint32 val1; //16
- uint32 zoneId; //17
- //char* name[16]; //18-33
- //uint32 name_flag; //34
- //char* name2[16]; //35-50
- //uint32 name_flag2; //51
- uint32 worldState; //52
- //uint32 val2; //53
+ uint32 id; //0
+ uint32 icon[11]; //1-11
+ float x; //12
+ float y; //13
+ float z; //14
+ uint32 mapId; //15
+ //uint32 val1; //16
+ uint32 zoneId; //17
+ //char const* name[16]; //18-33
+ //uint32 name_flag; //34
+ //char const* name2[16]; //35-50
+ //uint32 name_flag2; //51
+ uint32 worldState; //52
+ //uint32 val2; //53
};
struct AuctionHouseEntry
@@ -575,7 +575,7 @@ struct AuctionHouseEntry
uint32 faction; // 1 id of faction.dbc for player factions associated with city
uint32 depositPercent; // 2 1/3 from real
uint32 cutPercent; // 3
- //char* name[16]; // 4-19
+ //char const* name[16]; // 4-19
// 20 string flag, unused
};
@@ -589,7 +589,7 @@ struct BarberShopStyleEntry
{
uint32 Id; // 0
uint32 type; // 1 value 0 -> hair, value 2 -> facialhair
- //char* name[16]; // 2-17 name of hair style
+ //char const* name[16]; // 2-17 name of hair style
//uint32 name_flags; // 18
//uint32 unk_name[16]; // 19-34, all empty
//uint32 unk_flags; // 35
@@ -605,7 +605,7 @@ struct BattlemasterListEntry
int32 mapid[8]; // 1-8 mapid
uint32 type; // 9 (3 - BG, 4 - arena)
//uint32 canJoinAsGroup; // 10 (0 or 1)
- char* name[16]; // 11-26
+ char const* name[16]; // 11-26
//uint32 nameFlags // 27 string flag, unused
uint32 maxGroupSize; // 28 maxGroupSize, used for checking if queue as group
uint32 HolidayWorldStateId; // 29 new 3.1
@@ -631,9 +631,9 @@ struct CharTitlesEntry
{
uint32 ID; // 0, title ids, for example in Quest::GetCharTitleId()
//uint32 unk1; // 1 flags?
- char* nameMale[16]; // 2-17
+ char const* nameMale[16]; // 2-17
// 18 string flag, unused
- char* nameFemale[16]; // 19-34
+ char const* nameFemale[16]; // 19-34
// 35 string flag, unused
uint32 bit_index; // 36 used in PLAYER_CHOSEN_TITLE and 1<<index in PLAYER__FIELD_KNOWN_TITLES
};
@@ -642,9 +642,9 @@ struct ChatChannelsEntry
{
uint32 ChannelID; // 0
uint32 flags; // 1
- char* pattern[16]; // 3-18
+ char const* pattern[16]; // 3-18
// 19 string flags, unused
- //char* name[16]; // 20-35 unused
+ //char const* name[16]; // 20-35 unused
// 36 string flag, unused
};
@@ -654,17 +654,17 @@ struct ChrClassesEntry
// 1, unused
uint32 powerType; // 2
// 3-4, unused
- char* name[16]; // 5-20 unused
+ char const* name[16]; // 5-20 unused
// 21 string flag, unused
- //char* nameFemale[16]; // 21-36 unused, if different from base (male) case
+ //char const* nameFemale[16]; // 21-36 unused, if different from base (male) case
// 37 string flag, unused
- //char* nameNeutralGender[16]; // 38-53 unused, if different from base (male) case
+ //char const* nameNeutralGender[16]; // 38-53 unused, if different from base (male) case
// 54 string flag, unused
// 55, unused
uint32 spellfamily; // 56
// 57, unused
uint32 CinematicSequence; // 58 id from CinematicSequences.dbc
- uint32 expansion; // 59 (0 - original race, 1 - tbc addon, ...)
+ uint32 expansion; // 59 (0 - original race, 1 - tbc addon, ...)
};
struct ChrRacesEntry
@@ -680,11 +680,11 @@ struct ChrRacesEntry
// 8-11 unused
uint32 CinematicSequence; // 12 id from CinematicSequences.dbc
//uint32 unk_322; // 13 faction (0 alliance, 1 horde, 2 not available?)
- char* name[16]; // 14-29 used for DBC language detection/selection
+ char const* name[16]; // 14-29 used for DBC language detection/selection
// 30 string flags, unused
- //char* nameFemale[16]; // 31-46, if different from base (male) case
+ //char const* nameFemale[16]; // 31-46, if different from base (male) case
// 47 string flags, unused
- //char* nameNeutralGender[16]; // 48-63, if different from base (male) case
+ //char const* nameNeutralGender[16]; // 48-63, if different from base (male) case
// 64 string flags, unused
// 65-67 unused
uint32 expansion; // 68 (0 - original race, 1 - tbc addon, ...)
@@ -693,7 +693,7 @@ struct ChrRacesEntry
struct CinematicCameraEntry
{
uint32 id; // 0 index
- char* filename; // 1
+ char const* filename; // 1
uint32 soundid; // 2 in SoundEntries.dbc or 0
float base_x; // 3
float base_y; // 4
@@ -738,7 +738,7 @@ struct CreatureFamilyEntry
uint32 petFoodMask; // 7 m_petFoodMask
int32 petTalentType; // 8 m_petTalentType
// 9 m_categoryEnumID
- char* Name[16]; // 10-25 m_name_lang
+ char const* Name[16]; // 10-25 m_name_lang
// 26 string flags
// 27 m_iconFile
};
@@ -747,7 +747,7 @@ struct CreatureModelDataEntry
{
uint32 Id;
//uint32 Flags;
- //char* ModelPath[16]
+ //char const* ModelPath[16]
//uint32 Unk1;
float Scale; // Used in calculation of unit collision data
//int32 Unk2
@@ -777,7 +777,7 @@ struct CreatureSpellDataEntry
struct CreatureTypeEntry
{
uint32 ID; // 0 m_ID
- //char* Name[16]; // 1-16 name
+ //char const* Name[16]; // 1-16 name
// 17 string flags
//uint32 no_expirience; // 18 no exp? critters, non-combat pets, gas cloud.
};
@@ -787,7 +787,7 @@ struct CurrencyCategoryEntry
{
uint32 ID; // 0
uint32 Unk1; // 1 0 for known categories and 3 for unknown one (3.0.9)
- char* Name[16]; // 2-17 name
+ char const* Name[16]; // 2-17 name
// // 18 string flags
};
*/
@@ -830,7 +830,7 @@ struct DungeonEncounterEntry
uint32 difficulty; // 2 instance mode
//uint32 unk0; // 3
uint32 encounterIndex; // 4 encounter index for creating completed mask
- char* encounterName[16]; // 5-20 encounter name
+ char const* encounterName[16]; // 5-20 encounter name
//uint32 nameFlags; // 21
//uint32 unk1; // 22
};
@@ -850,7 +850,7 @@ struct DurabilityQualityEntry
struct EmotesEntry
{
uint32 Id; // 0
- //char* Name; // 1, internal name
+ //char const* Name; // 1, internal name
//uint32 AnimationId; // 2, ref to animationData
uint32 Flags; // 3, bitmask, may be unit_flags
uint32 EmoteType; // 4, Can be 0, 1 or 2 (determine how emote are shown)
@@ -877,9 +877,9 @@ struct FactionEntry
float spilloverRateOut; // 20 Faction outputs rep * spilloverRateOut as spillover reputation
uint32 spilloverMaxRankIn; // 21 The highest rank the faction will profit from incoming spillover
//uint32 spilloverRank_unk; // 22 It does not seem to be the max standing at which a faction outputs spillover ...so no idea
- char* name[16]; // 23-38 m_name_lang
+ char const* name[16]; // 23-38 m_name_lang
// 39 string flags
- //char* description[16]; // 40-55 m_description_lang
+ //char const* description[16]; // 40-55 m_description_lang
// 56 string flags
// helpers
@@ -953,15 +953,15 @@ struct FactionTemplateEntry
struct GameObjectDisplayInfoEntry
{
uint32 Displayid; // 0 m_ID
- char* filename; // 1
- //uint32 unk1[10]; //2-11
+ char const* filename; // 1
+ //uint32 unk1[10]; //2-11
float minX;
float minY;
float minZ;
float maxX;
float maxY;
float maxZ;
- //uint32 transport; //18
+ //uint32 transport; //18
};
struct GemPropertiesEntry
@@ -1056,7 +1056,7 @@ struct GtRegenMPPerSptEntry
struct HolidayDescriptionsEntry
{
uint32 ID; // 0, this is NOT holiday id
- //char* name[16] // 1-16 m_name_lang
+ //char const* name[16] // 1-16 m_name_lang
// 17 name flags
};
*/
@@ -1065,7 +1065,7 @@ struct HolidayDescriptionsEntry
struct HolidayNamesEntry
{
uint32 ID; // 0, this is NOT holiday id
- //char* name[16] // 1-16 m_name_lang
+ //char const* name[16] // 1-16 m_name_lang
// 17 name flags
};
*/
@@ -1084,7 +1084,7 @@ struct HolidaysEntry
uint32 CalendarFlags[MAX_HOLIDAY_FLAGS]; // 39-48 m_calendarFlags
//uint32 holidayNameId; // 49 m_holidayNameID (HolidayNames.dbc)
//uint32 holidayDescriptionId; // 50 m_holidayDescriptionID (HolidayDescriptions.dbc)
- char* TextureFilename; // 51 m_textureFilename
+ char const* TextureFilename; // 51 m_textureFilename
uint32 Priority; // 52 m_priority
int32 CalendarFilterType; // 53 m_calendarFilterType (-1 = Fishing Contest, 0 = Unk, 1 = Darkmoon Festival, 2 = Yearly holiday)
//uint32 flags; // 54 m_flags (0 = Darkmoon Faire, Fishing Contest and Wotlk Launch, rest is 1)
@@ -1093,7 +1093,7 @@ struct HolidaysEntry
struct ItemBagFamilyEntry
{
uint32 ID; // 0
- //char* name[16] // 1-16 m_name_lang
+ //char const* name[16] // 1-16 m_name_lang
// // 17 name flags
};
@@ -1102,7 +1102,7 @@ struct ItemDisplayInfoEntry
uint32 ID; // 0 m_ID
// 1 m_modelName[2]
// 2 m_modelTexture[2]
- char* inventoryIcon; // 3 m_inventoryIcon
+ char const* inventoryIcon; // 3 m_inventoryIcon
// 4 m_geosetGroup[3]
// 5 m_flags
// 6 m_spellVisualID
@@ -1137,7 +1137,7 @@ struct ItemExtendedCostEntry
struct ItemLimitCategoryEntry
{
uint32 ID; // 0 Id
- //char* name[16] // 1-16 m_name_lang
+ //char const* name[16] // 1-16 m_name_lang
// 17 name flags
uint32 maxCount; // 18, max allowed equipped as item or in gem slot
uint32 mode; // 19, 0 = have, 1 = equip (enum ItemLimitCategoryMode)
@@ -1147,21 +1147,24 @@ struct ItemLimitCategoryEntry
struct ItemRandomPropertiesEntry
{
- uint32 ID; // 0 m_ID
- //char* internalName // 1 m_Name
- uint32 enchant_id[MAX_ITEM_ENCHANTMENT_EFFECTS]; // 2-6 m_Enchantment
- char* nameSuffix[16]; // 7-22 m_name_lang
- // 23 name flags
+ uint32 ID; // 0
+ //char const* InternalName; // 1
+ std::array<uint32, MAX_ITEM_ENCHANTMENT_EFFECTS> Enchantment; // 2-4
+ //std::array<uint32, 2> UnusedEnchantment; // 5-6
+ std::array<char const*, 16> Name; // 7-22
+ //uint32 Name_lang_mask; // 23
};
struct ItemRandomSuffixEntry
{
- uint32 ID; // 0 m_ID
- char* nameSuffix[16]; // 1-16 m_name_lang
- // 17, name flags
- // 18 m_internalName
- uint32 enchant_id[MAX_ITEM_ENCHANTMENT_EFFECTS]; // 19-23 m_enchantment
- uint32 prefix[MAX_ITEM_ENCHANTMENT_EFFECTS]; // 24-28 m_allocationPct
+ uint32 ID; // 0
+ std::array<char const*, 16> Name; // 1-16
+ //uint32 Name_lang_mask; // 17
+ //char const* InternalName; // 18
+ std::array<uint32, MAX_ITEM_ENCHANTMENT_EFFECTS> Enchantment; // 19-21
+ //std::array<uint32, 2> UnusedEnchantment; // 22-23
+ std::array<uint32, MAX_ITEM_ENCHANTMENT_EFFECTS> AllocationPct; // 24-26
+ //std::array<uint32, 2> UnusedAllocationPct; // 27-28
};
#define MAX_ITEM_SET_ITEMS 10
@@ -1170,7 +1173,7 @@ struct ItemRandomSuffixEntry
struct ItemSetEntry
{
//uint32 id // 0 m_ID
- char* name[16]; // 1-16 m_name_lang
+ char const* name[16]; // 1-16 m_name_lang
// 17 string flags, unused
uint32 itemId[MAX_ITEM_SET_ITEMS]; // 18-27 m_itemID
//uint32 unknown[7]; // 28-34 unk, all 0
@@ -1183,7 +1186,7 @@ struct ItemSetEntry
struct LFGDungeonEntry
{
uint32 ID; // 0
- char* name[16]; // 1-17 Name lang
+ char const* name[16]; // 1-17 Name lang
uint32 minlevel; // 18
uint32 maxlevel; // 19
uint32 reclevel; // 20
@@ -1194,11 +1197,11 @@ struct LFGDungeonEntry
uint32 flags; // 25
uint32 type; // 26
//uint32 unk; // 27
- //char* iconname; // 28
+ //char const* iconname; // 28
uint32 expansion; // 29
//uint32 unk4; // 30
uint32 grouptype; // 31
- //char* desc[16]; // 32-47 Description
+ //char const* desc[16]; // 32-47 Description
// Helpers
[[nodiscard]] uint32 Entry() const { return ID + (type << 24); }
};
@@ -1225,7 +1228,7 @@ struct LightEntry
struct LiquidTypeEntry
{
uint32 Id;
- //char* Name;
+ //char const* Name;
//uint32 Flags;
uint32 Type;
//uint32 SoundId;
@@ -1239,7 +1242,7 @@ struct LiquidTypeEntry
//uint32 ParticleMovement;
//uint32 ParticleTexSlots;
//uint32 LiquidMaterialID;
- //char* Texture[6];
+ //char const* Texture[6];
//uint32 Color[2];
//float Unk1[18];
//uint32 Unk2[4];
@@ -1259,24 +1262,24 @@ struct LockEntry
struct MailTemplateEntry
{
uint32 ID; // 0
- //char* subject[16]; // 1-16
+ //char const* subject[16]; // 1-16
// 17 name flags, unused
- char* content[16]; // 18-33
+ char const* content[16]; // 18-33
};
struct MapEntry
{
uint32 MapID; // 0
- //char* internalname; // 1 unused
+ //char const* internalname; // 1 unused
uint32 map_type; // 2
uint32 Flags; // 3
// 4 0 or 1 for battlegrounds (not arenas)
- char* name[16]; // 5-20
+ char const* name[16]; // 5-20
// 21 name flags, unused
uint32 linked_zone; // 22 common zone for instance and continent map
- //char* hordeIntro[16]; // 23-38 text for PvP Zones
+ //char const* hordeIntro[16]; // 23-38 text for PvP Zones
// 39 intro text flags
- //char* allianceIntro[16]; // 40-55 text for PvP Zones
+ //char const* allianceIntro[16]; // 40-55 text for PvP Zones
// 56 intro text flags
uint32 multimap_id; // 57
//float BattlefieldMapIconScale; // 58
@@ -1323,17 +1326,17 @@ struct MapDifficultyEntry
//uint32 Id; // 0
uint32 MapId; // 1
uint32 Difficulty; // 2 (for arenas: arena slot)
- char* areaTriggerText; // 3-18 text showed when transfer to map failed (missing requirements)
+ char const* areaTriggerText; // 3-18 text showed when transfer to map failed (missing requirements)
//uint32 textFlags; // 19
uint32 resetTime; // 20
uint32 maxPlayers; // 21
- //char* difficultyString; // 22
+ //char const* difficultyString; // 22
};
struct MovieEntry
{
uint32 Id; // 0 index
- //char* filename; // 1
+ //char const* filename; // 1
//uint32 unk2; // 2 always 100
};
@@ -1350,7 +1353,7 @@ struct PowerDisplayEntry
{
uint32 Id; // 0
uint32 PowerType; // 1
- //char* Name; // 2
+ //char const* Name; // 2
//uint32 R; // 3
//uint32 G; // 4
//uint32 B; // 5
@@ -1373,7 +1376,7 @@ struct PvPDifficultyEntry
struct QuestSortEntry
{
uint32 id; // 0 m_ID
- //char* name[16]; // 1-16 m_SortName_lang
+ //char const* name[16]; // 1-16 m_SortName_lang
// 17 name flags
};
@@ -1479,7 +1482,7 @@ struct ScalingStatValuesEntry
//struct SkillLineCategoryEntry{
// uint32 id; // 0 m_ID
-// char* name[16]; // 1-17 m_name_lang
+// char const* name[16]; // 1-17 m_name_lang
// // 18 string flag
// uint32 displayOrder; // 19 m_sortIndex
//};
@@ -1503,12 +1506,12 @@ struct SkillLineEntry
uint32 id; // 0 m_ID
int32 categoryId; // 1 m_categoryID
//uint32 skillCostID; // 2 m_skillCostsID
- char* name[16]; // 3-18 m_displayName_lang
+ char const* name[16]; // 3-18 m_displayName_lang
// 19 string flags
- //char* description[16]; // 20-35 m_description_lang
+ //char const* description[16]; // 20-35 m_description_lang
// 36 string flags
uint32 spellIcon; // 37 m_spellIconID
- //char* alternateVerb[16]; // 38-53 m_alternateVerb_lang
+ //char const* alternateVerb[16]; // 38-53 m_alternateVerb_lang
// 54 string flags
uint32 canLink; // 55 m_canLink (prof. with recipes
};
@@ -1541,10 +1544,10 @@ struct SoundEntriesEntry
{
uint32 Id; // 0 m_ID
//uint32 Type; // 1 m_soundType
- //char* InternalName; // 2 m_name
- //char* FileName[10]; // 3-12 m_File[10]
+ //char const* InternalName; // 2 m_name
+ //char const* FileName[10]; // 3-12 m_File[10]
//uint32 Unk13[10]; // 13-22 m_Freq[10]
- //char* Path; // 23 m_DirectoryBase
+ //char const* Path; // 23 m_DirectoryBase
// 24 m_volumeFloat
// 25 m_flags
// 26 m_minDistance
@@ -1559,115 +1562,115 @@ struct SoundEntriesEntry
struct SpellEntry
{
- uint32 Id; // 0 m_ID
- uint32 Category; // 1 m_category
- uint32 Dispel; // 2 m_dispelType
- uint32 Mechanic; // 3 m_mechanic
- uint32 Attributes; // 4 m_attributes
- uint32 AttributesEx; // 5 m_attributesEx
- uint32 AttributesEx2; // 6 m_attributesExB
- uint32 AttributesEx3; // 7 m_attributesExC
- uint32 AttributesEx4; // 8 m_attributesExD
- uint32 AttributesEx5; // 9 m_attributesExE
- uint32 AttributesEx6; // 10 m_attributesExF
- uint32 AttributesEx7; // 11 m_attributesExG
- uint32 Stances; // 12 m_shapeshiftMask
- // uint32 unk_320_2; // 13 3.2.0
- uint32 StancesNot; // 14 m_shapeshiftExclude
- // uint32 unk_320_3; // 15 3.2.0
- uint32 Targets; // 16 m_targets
- uint32 TargetCreatureType; // 17 m_targetCreatureType
- uint32 RequiresSpellFocus; // 18 m_requiresSpellFocus
- uint32 FacingCasterFlags; // 19 m_facingCasterFlags
- uint32 CasterAuraState; // 20 m_casterAuraState
- uint32 TargetAuraState; // 21 m_targetAuraState
- uint32 CasterAuraStateNot; // 22 m_excludeCasterAuraState
- uint32 TargetAuraStateNot; // 23 m_excludeTargetAuraState
- uint32 CasterAuraSpell; // 24 m_casterAuraSpell
- uint32 TargetAuraSpell; // 25 m_targetAuraSpell
- uint32 ExcludeCasterAuraSpell; // 26 m_excludeCasterAuraSpell
- uint32 ExcludeTargetAuraSpell; // 27 m_excludeTargetAuraSpell
- uint32 CastingTimeIndex; // 28 m_castingTimeIndex
- uint32 RecoveryTime; // 29 m_recoveryTime
- uint32 CategoryRecoveryTime; // 30 m_categoryRecoveryTime
- uint32 InterruptFlags; // 31 m_interruptFlags
- uint32 AuraInterruptFlags; // 32 m_auraInterruptFlags
- uint32 ChannelInterruptFlags; // 33 m_channelInterruptFlags
- uint32 ProcFlags; // 34 m_procTypeMask
- uint32 ProcChance; // 35 m_procChance
- uint32 ProcCharges; // 36 m_procCharges
- uint32 MaxLevel; // 37 m_maxLevel
- uint32 BaseLevel; // 38 m_baseLevel
- uint32 SpellLevel; // 39 m_spellLevel
- uint32 DurationIndex; // 40 m_durationIndex
- uint32 PowerType; // 41 m_powerType
- uint32 ManaCost; // 42 m_manaCost
- uint32 ManaCostPerlevel; // 43 m_manaCostPerLevel
- uint32 ManaPerSecond; // 44 m_manaPerSecond
- uint32 ManaPerSecondPerLevel; // 45 m_manaPerSecondPerLeve
- uint32 RangeIndex; // 46 m_rangeIndex
- float Speed; // 47 m_speed
- //uint32 ModalNextSpell; // 48 m_modalNextSpell not used
- uint32 StackAmount; // 49 m_cumulativeAura
- uint32 Totem[2]; // 50-51 m_totem
- int32 Reagent[MAX_SPELL_REAGENTS]; // 52-59 m_reagent
- uint32 ReagentCount[MAX_SPELL_REAGENTS]; // 60-67 m_reagentCount
- int32 EquippedItemClass; // 68 m_equippedItemClass (value)
- int32 EquippedItemSubClassMask; // 69 m_equippedItemSubclass (mask)
- int32 EquippedItemInventoryTypeMask; // 70 m_equippedItemInvTypes (mask)
- uint32 Effect[MAX_SPELL_EFFECTS]; // 71-73 m_effect
- int32 EffectDieSides[MAX_SPELL_EFFECTS]; // 74-76 m_effectDieSides
- float EffectRealPointsPerLevel[MAX_SPELL_EFFECTS]; // 77-79 m_effectRealPointsPerLevel
- int32 EffectBasePoints[MAX_SPELL_EFFECTS]; // 80-82 m_effectBasePoints (must not be used in spell/auras explicitly, must be used cached Spell::m_currentBasePoints)
- uint32 EffectMechanic[MAX_SPELL_EFFECTS]; // 83-85 m_effectMechanic
- uint32 EffectImplicitTargetA[MAX_SPELL_EFFECTS]; // 86-88 m_implicitTargetA
- uint32 EffectImplicitTargetB[MAX_SPELL_EFFECTS]; // 89-91 m_implicitTargetB
- uint32 EffectRadiusIndex[MAX_SPELL_EFFECTS]; // 92-94 m_effectRadiusIndex - spellradius.dbc
- uint32 EffectApplyAuraName[MAX_SPELL_EFFECTS]; // 95-97 m_effectAura
- uint32 EffectAmplitude[MAX_SPELL_EFFECTS]; // 98-100 m_effectAuraPeriod
- float EffectValueMultiplier[MAX_SPELL_EFFECTS]; // 101-103
- uint32 EffectChainTarget[MAX_SPELL_EFFECTS]; // 104-106 m_effectChainTargets
- uint32 EffectItemType[MAX_SPELL_EFFECTS]; // 107-109 m_effectItemType
- int32 EffectMiscValue[MAX_SPELL_EFFECTS]; // 110-112 m_effectMiscValue
- int32 EffectMiscValueB[MAX_SPELL_EFFECTS]; // 113-115 m_effectMiscValueB
- uint32 EffectTriggerSpell[MAX_SPELL_EFFECTS]; // 116-118 m_effectTriggerSpell
- float EffectPointsPerComboPoint[MAX_SPELL_EFFECTS]; // 119-121 m_effectPointsPerCombo
- flag96 EffectSpellClassMask[MAX_SPELL_EFFECTS]; // 122-130
- uint32 SpellVisual[2]; // 131-132 m_spellVisualID
- uint32 SpellIconID; // 133 m_spellIconID
- uint32 ActiveIconID; // 134 m_activeIconID
- //uint32 SpellPriority; // 135 not used
- char* SpellName[16]; // 136-151 m_name_lang
- //uint32 SpellNameFlag; // 152 not used
- char* Rank[16]; // 153-168 m_nameSubtext_lang
- //uint32 RankFlags; // 169 not used
- //char* Description[16]; // 170-185 m_description_lang not used
- //uint32 DescriptionFlags; // 186 not used
- //char* ToolTip[16]; // 187-202 m_auraDescription_lang not used
- //uint32 ToolTipFlags; // 203 not used
- uint32 ManaCostPercentage; // 204 m_manaCostPct
- uint32 StartRecoveryCategory; // 205 m_startRecoveryCategory
- uint32 StartRecoveryTime; // 206 m_startRecoveryTime
- uint32 MaxTargetLevel; // 207 m_maxTargetLevel
- uint32 SpellFamilyName; // 208 m_spellClassSet
- flag96 SpellFamilyFlags; // 209-211
- uint32 MaxAffectedTargets; // 212 m_maxTargets
- uint32 DmgClass; // 213 m_defenseType
- uint32 PreventionType; // 214 m_preventionType
- //uint32 StanceBarOrder; // 215 m_stanceBarOrder not used
- float EffectDamageMultiplier[MAX_SPELL_EFFECTS]; // 216-218 m_effectChainAmplitude
- //uint32 MinFactionId; // 219 m_minFactionID not used
- //uint32 MinReputation; // 220 m_minReputation not used
- //uint32 RequiredAuraVision; // 221 m_requiredAuraVision not used
- uint32 TotemCategory[2]; // 222-223 m_requiredTotemCategoryID
- int32 AreaGroupId; // 224 m_requiredAreaGroupId
- uint32 SchoolMask; // 225 m_schoolMask
- uint32 RuneCostID; // 226 m_runeCostID
- //uint32 SpellMissileID; // 227 m_spellMissileID not used
- //uint32 PowerDisplayId; // 228 PowerDisplay.dbc, new in 3.1
- float EffectBonusMultiplier[MAX_SPELL_EFFECTS]; // 229-231 3.2.0
- //uint32 SpellDescriptionVariableID; // 232 3.2.0
- //uint32 SpellDifficultyId; // 233 3.3.0
+ uint32 Id; // 0 m_ID
+ uint32 Category; // 1 m_category
+ uint32 Dispel; // 2 m_dispelType
+ uint32 Mechanic; // 3 m_mechanic
+ uint32 Attributes; // 4 m_attributes
+ uint32 AttributesEx; // 5 m_attributesEx
+ uint32 AttributesEx2; // 6 m_attributesExB
+ uint32 AttributesEx3; // 7 m_attributesExC
+ uint32 AttributesEx4; // 8 m_attributesExD
+ uint32 AttributesEx5; // 9 m_attributesExE
+ uint32 AttributesEx6; // 10 m_attributesExF
+ uint32 AttributesEx7; // 11 m_attributesExG
+ uint32 Stances; // 12 m_shapeshiftMask
+ // uint32 unk_320_2; // 13 3.2.0
+ uint32 StancesNot; // 14 m_shapeshiftExclude
+ // uint32 unk_320_3; // 15 3.2.0
+ uint32 Targets; // 16 m_targets
+ uint32 TargetCreatureType; // 17 m_targetCreatureType
+ uint32 RequiresSpellFocus; // 18 m_requiresSpellFocus
+ uint32 FacingCasterFlags; // 19 m_facingCasterFlags
+ uint32 CasterAuraState; // 20 m_casterAuraState
+ uint32 TargetAuraState; // 21 m_targetAuraState
+ uint32 CasterAuraStateNot; // 22 m_excludeCasterAuraState
+ uint32 TargetAuraStateNot; // 23 m_excludeTargetAuraState
+ uint32 CasterAuraSpell; // 24 m_casterAuraSpell
+ uint32 TargetAuraSpell; // 25 m_targetAuraSpell
+ uint32 ExcludeCasterAuraSpell; // 26 m_excludeCasterAuraSpell
+ uint32 ExcludeTargetAuraSpell; // 27 m_excludeTargetAuraSpell
+ uint32 CastingTimeIndex; // 28 m_castingTimeIndex
+ uint32 RecoveryTime; // 29 m_recoveryTime
+ uint32 CategoryRecoveryTime; // 30 m_categoryRecoveryTime
+ uint32 InterruptFlags; // 31 m_interruptFlags
+ uint32 AuraInterruptFlags; // 32 m_auraInterruptFlags
+ uint32 ChannelInterruptFlags; // 33 m_channelInterruptFlags
+ uint32 ProcFlags; // 34 m_procTypeMask
+ uint32 ProcChance; // 35 m_procChance
+ uint32 ProcCharges; // 36 m_procCharges
+ uint32 MaxLevel; // 37 m_maxLevel
+ uint32 BaseLevel; // 38 m_baseLevel
+ uint32 SpellLevel; // 39 m_spellLevel
+ uint32 DurationIndex; // 40 m_durationIndex
+ uint32 PowerType; // 41 m_powerType
+ uint32 ManaCost; // 42 m_manaCost
+ uint32 ManaCostPerlevel; // 43 m_manaCostPerLevel
+ uint32 ManaPerSecond; // 44 m_manaPerSecond
+ uint32 ManaPerSecondPerLevel; // 45 m_manaPerSecondPerLeve
+ uint32 RangeIndex; // 46 m_rangeIndex
+ float Speed; // 47 m_speed
+ //uint32 ModalNextSpell; // 48 m_modalNextSpell not used
+ uint32 StackAmount; // 49 m_cumulativeAura
+ std::array<uint32, 2> Totem; // 50-51 m_totem
+ std::array<int32, MAX_SPELL_REAGENTS> Reagent; // 52-59 m_reagent
+ std::array<uint32, MAX_SPELL_REAGENTS> ReagentCount; // 60-67 m_reagentCount
+ int32 EquippedItemClass; // 68 m_equippedItemClass (value)
+ int32 EquippedItemSubClassMask; // 69 m_equippedItemSubclass (mask)
+ int32 EquippedItemInventoryTypeMask; // 70 m_equippedItemInvTypes (mask)
+ std::array<uint32, MAX_SPELL_EFFECTS> Effect; // 71-73 m_effect
+ std::array<int32, MAX_SPELL_EFFECTS> EffectDieSides; // 74-76 m_effectDieSides
+ std::array<float, MAX_SPELL_EFFECTS> EffectRealPointsPerLevel; // 77-79 m_effectRealPointsPerLevel
+ std::array<int32, MAX_SPELL_EFFECTS> EffectBasePoints; // 80-82 m_effectBasePoints (must not be used in spell/auras explicitly, must be used cached Spell::m_currentBasePoints)
+ std::array<uint32, MAX_SPELL_EFFECTS> EffectMechanic; // 83-85 m_effectMechanic
+ std::array<uint32, MAX_SPELL_EFFECTS> EffectImplicitTargetA; // 86-88 m_implicitTargetA
+ std::array<uint32, MAX_SPELL_EFFECTS> EffectImplicitTargetB; // 89-91 m_implicitTargetB
+ std::array<uint32, MAX_SPELL_EFFECTS> EffectRadiusIndex; // 92-94 m_effectRadiusIndex - spellradius.dbc
+ std::array<uint32, MAX_SPELL_EFFECTS> EffectApplyAuraName; // 95-97 m_effectAura
+ std::array<uint32, MAX_SPELL_EFFECTS> EffectAmplitude; // 98-100 m_effectAuraPeriod
+ std::array<float, MAX_SPELL_EFFECTS> EffectValueMultiplier; // 101-103
+ std::array<uint32, MAX_SPELL_EFFECTS> EffectChainTarget; // 104-106 m_effectChainTargets
+ std::array<uint32, MAX_SPELL_EFFECTS> EffectItemType; // 107-109 m_effectItemType
+ std::array<int32, MAX_SPELL_EFFECTS> EffectMiscValue; // 110-112 m_effectMiscValue
+ std::array<int32, MAX_SPELL_EFFECTS> EffectMiscValueB; // 113-115 m_effectMiscValueB
+ std::array<uint32, MAX_SPELL_EFFECTS> EffectTriggerSpell; // 116-118 m_effectTriggerSpell
+ std::array<float, MAX_SPELL_EFFECTS> EffectPointsPerComboPoint; // 119-121 m_effectPointsPerCombo
+ std::array<flag96, MAX_SPELL_EFFECTS> EffectSpellClassMask; // 122-130
+ std::array<uint32, 2> SpellVisual; // 131-132 m_spellVisualID
+ uint32 SpellIconID; // 133 m_spellIconID
+ uint32 ActiveIconID; // 134 m_activeIconID
+ //uint32 SpellPriority; // 135 not used
+ std::array<char const*, 16> SpellName; // 136-151 m_name_lang
+ //uint32 SpellNameFlag; // 152 not used
+ std::array<char const*, 16> Rank; // 153-168 m_nameSubtext_lang
+ //uint32 RankFlags; // 169 not used
+ //char const* Description[16]; // 170-185 m_description_lang not used
+ //uint32 DescriptionFlags; // 186 not used
+ //char const* ToolTip[16]; // 187-202 m_auraDescription_lang not used
+ //uint32 ToolTipFlags; // 203 not used
+ uint32 ManaCostPercentage; // 204 m_manaCostPct
+ uint32 StartRecoveryCategory; // 205 m_startRecoveryCategory
+ uint32 StartRecoveryTime; // 206 m_startRecoveryTime
+ uint32 MaxTargetLevel; // 207 m_maxTargetLevel
+ uint32 SpellFamilyName; // 208 m_spellClassSet
+ flag96 SpellFamilyFlags; // 209-211
+ uint32 MaxAffectedTargets; // 212 m_maxTargets
+ uint32 DmgClass; // 213 m_defenseType
+ uint32 PreventionType; // 214 m_preventionType
+ //uint32 StanceBarOrder; // 215 m_stanceBarOrder not used
+ std::array<float, MAX_SPELL_EFFECTS> EffectDamageMultiplier; // 216-218 m_effectChainAmplitude
+ //uint32 MinFactionId; // 219 m_minFactionID not used
+ //uint32 MinReputation; // 220 m_minReputation not used
+ //uint32 RequiredAuraVision; // 221 m_requiredAuraVision not used
+ std::array<uint32, 2> TotemCategory; // 222-223 m_requiredTotemCategoryID
+ int32 AreaGroupId; // 224 m_requiredAreaGroupId
+ uint32 SchoolMask; // 225 m_schoolMask
+ uint32 RuneCostID; // 226 m_runeCostID
+ //uint32 SpellMissileID; // 227 m_spellMissileID not used
+ //uint32 PowerDisplayId; // 228 PowerDisplay.dbc, new in 3.1
+ std::array<float, MAX_SPELL_EFFECTS> EffectBonusMultiplier; // 229-231 3.2.0
+ //uint32 SpellDescriptionVariableID; // 232 3.2.0
+ //uint32 SpellDifficultyId; // 233 3.3.0
};
typedef std::set<std::pair<bool, uint32>> SpellCategorySet;
@@ -1698,7 +1701,7 @@ struct SpellDifficultyEntry
struct SpellFocusObjectEntry
{
uint32 ID; // 0
- //char* Name[16]; // 1-15 unused
+ //char const* Name[16]; // 1-15 unused
// 16 string flags, unused
};
@@ -1718,9 +1721,9 @@ struct SpellRangeEntry
float maxRangeHostile;
float maxRangeFriend;
uint32 type;
- //char* Name[16]; // 7-23 unused
+ //char const* Name[16]; // 7-23 unused
// 24 string flags, unused
- //char* Name2[16]; // 25-40 unused
+ //char const* Name2[16]; // 25-40 unused
// 41 string flags, unused
};
@@ -1740,7 +1743,7 @@ struct SpellShapeshiftEntry
{
uint32 ID; // 0
//uint32 buttonPosition; // 1 unused
- //char* Name[16]; // 2-17 unused
+ //char const* Name[16]; // 2-17 unused
//uint32 NameFlags; // 18 unused
uint32 flags1; // 19
int32 creatureType; // 20 <= 0 humanoid, other normal creature types
@@ -1769,7 +1772,7 @@ struct SpellItemEnchantmentEntry
uint32 amount[MAX_SPELL_ITEM_ENCHANTMENT_EFFECTS]; // 5-7 m_effectPointsMin[MAX_SPELL_ITEM_ENCHANTMENT_EFFECTS]
//uint32 amount2[MAX_SPELL_ITEM_ENCHANTMENT_EFFECTS] // 8-10 m_effectPointsMax[MAX_SPELL_ITEM_ENCHANTMENT_EFFECTS]
uint32 spellid[MAX_SPELL_ITEM_ENCHANTMENT_EFFECTS]; // 11-13 m_effectArg[MAX_SPELL_ITEM_ENCHANTMENT_EFFECTS]
- char* description[16]; // 14-29 m_name_lang[16]
+ char const* description[16]; // 14-29 m_name_lang[16]
//uint32 descriptionFlags; // 30 name flags
uint32 aura_id; // 31 m_itemVisual
uint32 slot; // 32 m_flags
@@ -1849,7 +1852,7 @@ struct TalentEntry
uint32 TalentTab; // 1 index in TalentTab.dbc (TalentTabEntry)
uint32 Row; // 2
uint32 Col; // 3
- uint32 RankID[MAX_TALENT_RANK]; // 4-8
+ std::array<uint32, MAX_TALENT_RANK> RankID; // 4-8
// 9-12 not used, always 0, maybe not used high ranks
uint32 DependsOn; // 13 index in Talent.dbc (TalentEntry)
// 14-15 not used
@@ -1863,14 +1866,14 @@ struct TalentEntry
struct TalentTabEntry
{
uint32 TalentTabID; // 0
- //char* name[16]; // 1-16, unused
+ //char const* name[16]; // 1-16, unused
//uint32 nameFlags; // 17, unused
//unit32 spellicon; // 18
// 19 not used
uint32 ClassMask; // 20
uint32 petTalentMask; // 21
uint32 tabpage; // 22
- //char* internalname; // 23
+ //char const* internalname; // 23
};
struct TaxiNodesEntry
@@ -1880,7 +1883,7 @@ struct TaxiNodesEntry
float x; // 2 m_x
float y; // 3 m_y
float z; // 4 m_z
- char* name[16]; // 5-21 m_Name_lang
+ char const* name[16]; // 5-21 m_Name_lang
// 22 string flags
uint32 MountCreatureID[2]; // 23-24 m_MountCreatureID[2]
};
@@ -1917,7 +1920,7 @@ struct TeamContributionPointsEntry
struct TotemCategoryEntry
{
uint32 ID; // 0
- //char* name[16]; // 1-16
+ //char const* name[16]; // 1-16
// 17 string flags, unused
uint32 categoryType; // 18 (one for specialization)
uint32 categoryMask; // 19 (compatibility mask for same type: different for totems, compatible from high to low for rods)
@@ -1960,8 +1963,8 @@ struct VehicleEntry
float m_cameraFadeDistScalarMin; // 15
float m_cameraFadeDistScalarMax; // 16
float m_cameraPitchOffset; // 17
- //int32 m_powerType[3]; // removed in 3.1
- //int32 m_powerToken[3]; // removed in 3.1
+ //int32 m_powerType[3]; // removed in 3.1
+ //int32 m_powerToken[3]; // removed in 3.1
float m_facingLimitRight; // 18
float m_facingLimitLeft; // 19
float m_msslTrgtTurnLingering; // 20
@@ -1972,9 +1975,9 @@ struct VehicleEntry
float m_msslTrgtArcRepeat; // 25
float m_msslTrgtArcWidth; // 26
float m_msslTrgtImpactRadius[2]; // 27-28
- char* m_msslTrgtArcTexture; // 29
- char* m_msslTrgtImpactTexture; // 30
- char* m_msslTrgtImpactModel[2]; // 31-32
+ char const* m_msslTrgtArcTexture; // 29
+ char const* m_msslTrgtImpactTexture; // 30
+ char const* m_msslTrgtImpactModel[2]; // 31-32
float m_cameraYawOffset; // 33
uint32 m_uiLocomotionType; // 34
float m_msslTrgtImpactTexRadius; // 35
@@ -2073,7 +2076,7 @@ struct WorldMapAreaEntry
//uint32 ID; // 0
uint32 map_id; // 1
uint32 area_id; // 2 index (continent 0 areas ignored)
- //char* internal_name // 3
+ //char const* internal_name // 3
float y1; // 4
float y2; // 5
float x1; // 6
@@ -2091,7 +2094,7 @@ struct WorldMapOverlayEntry
//uint32 worldMapAreaId; // 1 idx in WorldMapArea.dbc
uint32 areatableID[MAX_WORLD_MAP_OVERLAY_AREA_IDX]; // 2-5
// 6-7 always 0, possible part of areatableID[]
- //char* internal_name // 8
+ //char const* internal_name // 8
// 9-16 some ints
};
@@ -2118,9 +2121,9 @@ struct WorldStateUI
uint32 zone; // 2 Can be zero for "everywhere".
uint32 phaseMask; // 3 Phase this WorldState is avaliable in
uint32 icon; // 4 The icon that is used in the interface.
- char* textureFilename; // 5
- char* text; // 6-21 The worldstate text
- char* description; // 22-38 Text shown when hovering mouse on icon
+ char const* textureFilename; // 5
+ char const* text; // 6-21 The worldstate text
+ char const* description; // 22-38 Text shown when hovering mouse on icon
uint32 worldstateID; // 39 This is the actual ID used
uint32 type; // 40 0 = unknown, 1 = unknown, 2 = not shown in ui, 3 = wintergrasp
uint32 unk1; // 41
diff --git a/src/server/shared/SharedDefines.h b/src/server/shared/SharedDefines.h
index 55ae573a49..c19f209b80 100644
--- a/src/server/shared/SharedDefines.h
+++ b/src/server/shared/SharedDefines.h
@@ -278,6 +278,16 @@ const uint32 ItemQualityColors[MAX_ITEM_QUALITY] =
0xffe6cc80 //LIGHT YELLOW
};
+size_t constexpr MAX_QUEST_DIFFICULTY = 5;
+uint32 constexpr QuestDifficultyColors[MAX_QUEST_DIFFICULTY] =
+{
+ 0xff40c040,
+ 0xff808080,
+ 0xffffff00,
+ 0xffff8040,
+ 0xffff2020
+};
+
// ***********************************
// Spell Attributes definitions
// ***********************************
diff --git a/src/server/worldserver/ACSoap/ACSoap.h b/src/server/worldserver/ACSoap/ACSoap.h
index 38e2cc0bbd..fd62012b58 100644
--- a/src/server/worldserver/ACSoap/ACSoap.h
+++ b/src/server/worldserver/ACSoap/ACSoap.h
@@ -33,7 +33,7 @@ public:
~SOAPCommand() { }
- void appendToPrintBuffer(char const* msg)
+ void appendToPrintBuffer(std::string_view msg)
{
m_printBuffer += msg;
}
@@ -49,7 +49,7 @@ public:
return m_success;
}
- static void print(void* callbackArg, char const* msg)
+ static void print(void* callbackArg, std::string_view msg)
{
((SOAPCommand*)callbackArg)->appendToPrintBuffer(msg);
}
diff --git a/src/server/worldserver/CommandLine/CliRunnable.cpp b/src/server/worldserver/CommandLine/CliRunnable.cpp
index 598b964b35..c75c439dfe 100644
--- a/src/server/worldserver/CommandLine/CliRunnable.cpp
+++ b/src/server/worldserver/CommandLine/CliRunnable.cpp
@@ -19,19 +19,22 @@
/// @{
/// \file
-#include "AccountMgr.h"
-#include "Chat.h"
-#include "CliRunnable.h"
#include "Common.h"
+#include "Errors.h"
+#include "ObjectMgr.h"
+#include "World.h"
#include "Config.h"
-#include "Language.h"
+#include "CliRunnable.h"
#include "Log.h"
-#include "MapMgr.h"
-#include "ObjectMgr.h"
-#include "Player.h"
#include "Util.h"
-#include "World.h"
-#include "WorldSession.h"
+
+#if AC_PLATFORM != AC_PLATFORM_WINDOWS
+#include "Chat.h"
+#include "ChatCommand.h"
+#include <cstring>
+#include <readline/readline.h>
+#include <readline/history.h>
+#endif
static constexpr char CLI_PREFIX[] = "AC> ";
@@ -41,75 +44,49 @@ static inline void PrintCliPrefix()
}
#if AC_PLATFORM != AC_PLATFORM_WINDOWS
-#include <readline/readline.h>
-#include <readline/history.h>
-
-char* command_finder(const char* text, int state)
+namespace Acore::Impl::Readline
{
- static size_t idx, len;
- const char* ret;
- std::vector<ChatCommand> const& cmd = ChatHandler::getCommandTable();
-
- if (!state)
+ static std::vector<std::string> vec;
+ char* cli_unpack_vector(char const*, int state)
{
- idx = 0;
- len = strlen(text);
+ static size_t i=0;
+ if (!state)
+ i = 0;
+ if (i < vec.size())
+ return strdup(vec[i++].c_str());
+ else
+ return nullptr;
}
- while (idx < cmd.size())
+ char** cli_completion(char const* text, int /*start*/, int /*end*/)
{
- ret = cmd[idx].Name;
- if (!cmd[idx].AllowConsole)
- {
- ++idx;
- continue;
- }
-
- ++idx;
- //printf("Checking %s \n", cmd[idx].Name);
- if (strncmp(ret, text, len) == 0)
- return strdup(ret);
+ ::rl_attempted_completion_over = 1;
+ vec = Acore::ChatCommands::GetAutoCompletionsFor(CliHandler(nullptr,nullptr), text);
+ return ::rl_completion_matches(text, &cli_unpack_vector);
}
- return ((char*)nullptr);
-}
-
-char** cli_completion(const char* text, int start, int /*end*/)
-{
- char** matches = nullptr;
-
- if (start)
- rl_bind_key('\t', rl_abort);
- else
- matches = rl_completion_matches((char*)text, &command_finder);
- return matches;
-}
-
-int cli_hook_func()
-{
- if (World::IsStopped())
- rl_done = 1;
- return 0;
+ int cli_hook_func()
+ {
+ if (World::IsStopped())
+ ::rl_done = 1;
+ return 0;
+ }
}
-
#endif
-void utf8print(void* /*arg*/, const char* str)
+void utf8print(void* /*arg*/, std::string_view str)
{
#if AC_PLATFORM == AC_PLATFORM_WINDOWS
- wchar_t wtemp_buf[6000];
- size_t wtemp_len = 6000 - 1;
- if (!Utf8toWStr(str, strlen(str), wtemp_buf, wtemp_len))
+ std::wstring wbuf;
+ if (!Utf8toWStr(str, wbuf))
return;
- char temp_buf[6000];
- CharToOemBuffW(&wtemp_buf[0], &temp_buf[0], wtemp_len + 1);
- printf(temp_buf);
+ wprintf(L"%s", wbuf.c_str());
#else
- {
- printf("%s", str);
- fflush(stdout);
- }
+{
+ printf(STRING_VIEW_FMT, STRING_VIEW_FMT_ARG(str));
+ fflush(stdout);
+}
#endif
}
@@ -129,7 +106,7 @@ int kb_hit_return()
tv.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
- select(STDIN_FILENO + 1, &fds, nullptr, nullptr, &tv);
+ select(STDIN_FILENO+1, &fds, nullptr, nullptr, &tv);
return FD_ISSET(STDIN_FILENO, &fds);
}
#endif
@@ -137,15 +114,21 @@ int kb_hit_return()
/// %Thread start
void CliThread()
{
- ///- Display the list of available CLI functions then beep
- //TC_LOG_INFO("server.worldserver", "");
-#if AC_PLATFORM != AC_PLATFORM_WINDOWS
- rl_attempted_completion_function = cli_completion;
- rl_event_hook = cli_hook_func;
+#if AC_PLATFORM == AC_PLATFORM_WINDOWS
+ // print this here the first time
+ // later it will be printed after command queue updates
+ PrintCliPrefix();
+#else
+ ::rl_attempted_completion_function = &Acore::Impl::Readline::cli_completion;
+ {
+ static char BLANK = '\0';
+ ::rl_completer_word_break_characters = &BLANK;
+ }
+ ::rl_event_hook = &Acore::Impl::Readline::cli_hook_func;
#endif
if (sConfigMgr->GetOption<bool>("BeepAtStart", true))
- printf("\a"); // \a = Alert
+ printf("\a"); // \a = Alert
#if AC_PLATFORM == AC_PLATFORM_WINDOWS
if (sConfigMgr->GetOption<bool>("FlashAtStart", true))
@@ -160,60 +143,53 @@ void CliThread()
}
#endif
- // print this here the first time
- // later it will be printed after command queue updates
- PrintCliPrefix();
-
///- As long as the World is running (no World::m_stopEvent), get the command line and handle it
while (!World::IsStopped())
{
fflush(stdout);
- char* command_str ; // = fgets(commandbuf, sizeof(commandbuf), stdin);
+ std::string command;
#if AC_PLATFORM == AC_PLATFORM_WINDOWS
- char commandbuf[256];
- command_str = fgets(commandbuf, sizeof(commandbuf), stdin);
-#else
- command_str = readline(CLI_PREFIX);
- rl_bind_key('\t', rl_complete);
-#endif
-
- if (command_str != nullptr)
+ wchar_t commandbuf[256];
+ if (fgetws(commandbuf, sizeof(commandbuf), stdin))
{
- for (int x = 0; command_str[x]; ++x)
- if (command_str[x] == '\r' || command_str[x] == '\n')
- {
- command_str[x] = 0;
- break;
- }
-
- if (!*command_str)
+ if (!WStrToUtf8(commandbuf, wcslen(commandbuf), command))
{
-#if AC_PLATFORM == AC_PLATFORM_WINDOWS
PrintCliPrefix();
-#else
- free(command_str);
-#endif
continue;
}
+ }
+#else
+ char* command_str = readline(CLI_PREFIX);
+ ::rl_bind_key('\t', ::rl_complete);
+ if (command_str != nullptr)
+ {
+ command = command_str;
+ free(command_str);
+ }
+#endif
- std::string command;
- if (!consoleToUtf8(command_str, command)) // convert from console encoding to utf8
+ if (!command.empty())
+ {
+ std::size_t nextLineIndex = command.find_first_of("\r\n");
+ if (nextLineIndex != std::string::npos)
{
+ if (nextLineIndex == 0)
+ {
#if AC_PLATFORM == AC_PLATFORM_WINDOWS
- PrintCliPrefix();
-#else
- free(command_str);
+ PrintCliPrefix();
#endif
- continue;
+ continue;
+ }
+
+ command.erase(nextLineIndex);
}
fflush(stdout);
sWorld->QueueCliCommand(new CliCommandHolder(nullptr, command.c_str(), &utf8print, &commandFinished));
#if AC_PLATFORM != AC_PLATFORM_WINDOWS
add_history(command.c_str());
- free(command_str);
#endif
}
else if (feof(stdin))
diff --git a/src/server/worldserver/RemoteAccess/RASession.cpp b/src/server/worldserver/RemoteAccess/RASession.cpp
index 728d984bd2..920445cb15 100644
--- a/src/server/worldserver/RemoteAccess/RASession.cpp
+++ b/src/server/worldserver/RemoteAccess/RASession.cpp
@@ -91,7 +91,7 @@ void RASession::Start()
_socket.close();
}
-int RASession::Send(const char* data)
+int RASession::Send(std::string_view data)
{
std::ostream os(&_writeBuffer);
os << data;
@@ -206,9 +206,9 @@ bool RASession::ProcessCommand(std::string& command)
return false;
}
-void RASession::CommandPrint(void* callbackArg, const char* text)
+void RASession::CommandPrint(void* callbackArg, std::string_view text)
{
- if (!text || !*text)
+ if (text.empty())
{
return;
}
diff --git a/src/server/worldserver/RemoteAccess/RASession.h b/src/server/worldserver/RemoteAccess/RASession.h
index 201b45528a..c4dd6d861f 100644
--- a/src/server/worldserver/RemoteAccess/RASession.h
+++ b/src/server/worldserver/RemoteAccess/RASession.h
@@ -40,13 +40,13 @@ public:
unsigned short GetRemotePort() const { return _socket.remote_endpoint().port(); }
private:
- int Send(const char* data);
+ int Send(std::string_view data);
std::string ReadString();
bool CheckAccessLevel(const std::string& user);
bool CheckPassword(const std::string& user, const std::string& pass);
bool ProcessCommand(std::string& command);
- static void CommandPrint(void* callbackArg, const char* text);
+ static void CommandPrint(void* callbackArg, std::string_view text);
static void CommandFinished(void* callbackArg, bool);
tcp::socket _socket;
diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist
index 801bdcd1da..845e26c42c 100644
--- a/src/server/worldserver/worldserver.conf.dist
+++ b/src/server/worldserver/worldserver.conf.dist
@@ -1788,30 +1788,34 @@ NpcRegenHPTimeIfTargetIsUnreachable = 10
# Collapses multiple subsequent whitespaces into a single whitespace.
# Not applied to the addon language, but may break old addons that use
# "normal" chat messages for sending data to other clients.
-# Default: 0 - (Disabled)
-# 1 - (Enabled)
+# Default: 1 - (Enabled, Blizzlike)
+# 0 - (Disabled)
+#
-ChatFakeMessagePreventing = 0
+ChatFakeMessagePreventing = 1
#
# ChatStrictLinkChecking.Severity
# Description: Check chat messages for in-game links to spells, items, quests, etc.
-# Default: 0 - (Disabled)
-# 1 - (Enabled, Check if only valid pipe commands are used, Prevents posting
-# pictures.)
-# 2 - (Enabled, Verify that pipe commands are used in a correct order)
-# 3 - (Check if color, entry and name don't contradict each other. For this to
-# work correctly, please assure that you have extracted locale DBCs of
-# every language specific client playing on this server)
+# -1 - (Only verify validity of link data, but permit use of custom colors)
+# Default: 0 - (Only verify that link data and color are valid without checking text)
+# 1 - (Additionally verifies that the link text matches the provided data)
+#
+# Note: If this is set to '1', you must additionally provide .dbc files for all
+# client locales that are in use on your server.
+# If any files are missing, messages with links from clients using those
+# locales will likely be blocked by the server.
+#
ChatStrictLinkChecking.Severity = 0
#
# ChatStrictLinkChecking.Kick
-# Description: Defines what should be done if a message is considered to contain invalid
-# pipe commands.
+# Description: Defines what should be done if a message containing invalid control characters
+# is received.
# Default: 0 - (Silently ignore message)
-# 1 - (Disconnect players who sent malformed messages)
+# 1 - (Ignore message and kick player)
+#
ChatStrictLinkChecking.Kick = 0