aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTreeston <treeston.mmoc@gmail.com>2020-09-12 19:42:10 +0200
committerShauren <shauren.trinity@gmail.com>2022-02-05 23:42:15 +0100
commit378691aaa286822ee20e0428d278f2bca6e7438e (patch)
tree82ee5ee5466dc5a593ab7aef9875c89e675dc63e
parent7d0586812a8e7d17f901d9e01af424c4a4ff6bc7 (diff)
[3.3.5] Core/ChatCommands: Show error messages from argument parsers (PR #25443)
(cherry picked from commit 75f9e7396e35360f3016cc0cb21e72e20f5d96d5)
-rw-r--r--sql/updates/world/master/2022_02_05_18_world_2020_09_12_04_world.sql20
-rw-r--r--src/common/Define.h3
-rw-r--r--src/common/Utilities/Util.cpp21
-rw-r--r--src/common/Utilities/Util.h13
-rw-r--r--src/server/game/Chat/Chat.cpp6
-rw-r--r--src/server/game/Chat/Chat.h8
-rw-r--r--src/server/game/Chat/ChatCommands/ChatCommand.h70
-rw-r--r--src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp63
-rw-r--r--src/server/game/Chat/ChatCommands/ChatCommandArgs.h158
-rw-r--r--src/server/game/Chat/ChatCommands/ChatCommandHelpers.cpp30
-rw-r--r--src/server/game/Chat/ChatCommands/ChatCommandHelpers.h45
-rw-r--r--src/server/game/Chat/ChatCommands/ChatCommandTags.cpp32
-rw-r--r--src/server/game/Chat/ChatCommands/ChatCommandTags.h37
-rw-r--r--src/server/game/Miscellaneous/Language.h33
-rw-r--r--src/server/game/World/World.h4
-rw-r--r--src/server/scripts/Commands/cs_gobject.cpp4
-rw-r--r--src/server/scripts/Commands/cs_npc.cpp4
-rw-r--r--src/server/worldserver/CommandLine/CliRunnable.cpp11
-rw-r--r--src/server/worldserver/RemoteAccess/RASession.cpp6
-rw-r--r--src/server/worldserver/RemoteAccess/RASession.h4
-rw-r--r--src/server/worldserver/TCSoap/TCSoap.h4
-rw-r--r--tests/game/ChatCommand.cpp11
22 files changed, 381 insertions, 206 deletions
diff --git a/sql/updates/world/master/2022_02_05_18_world_2020_09_12_04_world.sql b/sql/updates/world/master/2022_02_05_18_world_2020_09_12_04_world.sql
new file mode 100644
index 00000000000..5ce6fd55dc4
--- /dev/null
+++ b/sql/updates/world/master/2022_02_05_18_world_2020_09_12_04_world.sql
@@ -0,0 +1,20 @@
+--
+DELETE FROM `trinity_string` WHERE `entry` BETWEEN 1500 AND 1516;
+INSERT INTO `trinity_string` (`entry`,`content_default`) VALUES
+(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.'),
+(1516, 'Currency ID %u does not exist.');
diff --git a/src/common/Define.h b/src/common/Define.h
index 1e73dd3069c..b43dfbf610c 100644
--- a/src/common/Define.h
+++ b/src/common/Define.h
@@ -144,6 +144,9 @@
#define SZFMTD "%" PRIuPTR
+#define STRING_VIEW_FMT "%.*s"
+#define STRING_VIEW_FMT_ARG(str) static_cast<int>((str).length()), (str).data()
+
typedef int64_t int64;
typedef int32_t int32;
typedef int16_t int16;
diff --git a/src/common/Utilities/Util.cpp b/src/common/Utilities/Util.cpp
index 511e4b99684..ad98bd5e491 100644
--- a/src/common/Utilities/Util.cpp
+++ b/src/common/Utilities/Util.cpp
@@ -21,6 +21,7 @@
#include "IpAddress.h"
#include "StringConvert.h"
#include "StringFormat.h"
+#include <boost/core/demangle.hpp>
#include <utf8.h>
#include <algorithm>
#include <iomanip>
@@ -831,19 +832,25 @@ void Trinity::Impl::HexStrToByteArray(std::string_view str, uint8* out, size_t o
}
}
-bool StringEqualI(std::string_view str1, std::string_view str2)
+bool StringEqualI(std::string_view a, std::string_view b)
{
- return std::equal(str1.begin(), str1.end(), str2.begin(), str2.end(),
- [](char a, char b)
- {
- return std::tolower(a) == std::tolower(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 StringCompareLessI(std::string_view a, std::string_view b)
+{
+ 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());
}
float DegToRad(float degrees)
diff --git a/src/common/Utilities/Util.h b/src/common/Utilities/Util.h
index 61f3d17acf8..8a24981c837 100644
--- a/src/common/Utilities/Util.h
+++ b/src/common/Utilities/Util.h
@@ -25,6 +25,7 @@
#include <array>
#include <string>
#include <string_view>
+#include <typeinfo>
#include <utility>
#include <vector>
@@ -377,6 +378,12 @@ inline bool ValueContainsStringI(std::pair<T, std::string_view> const& haystack,
{
return StringContainsStringI(haystack.second, needle);
}
+TC_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>
@@ -599,6 +606,12 @@ Ret* Coalesce(T1* first, T*... rest)
return static_cast<Ret*>(first);
}
+TC_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)); }
+
template<typename T>
struct NonDefaultConstructible
{
diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp
index ec572e72704..dc26c3cf0c7 100644
--- a/src/server/game/Chat/Chat.cpp
+++ b/src/server/game/Chat/Chat.cpp
@@ -175,7 +175,7 @@ bool ChatHandler::hasStringAbbr(const char* name, const char* part)
return true;
}
-void ChatHandler::SendSysMessage(const char *str, bool escapeCharacters)
+void ChatHandler::SendSysMessage(std::string_view str, bool escapeCharacters)
{
std::string msg{ str };
@@ -998,7 +998,7 @@ bool CliHandler::isAvailable(ChatCommand const& cmd) const
return cmd.AllowConsole;
}
-void CliHandler::SendSysMessage(const char *str, bool /*escapeCharacters*/)
+void CliHandler::SendSysMessage(std::string_view str, bool /*escapeCharacters*/)
{
m_print(m_callbackArg, str);
m_print(m_callbackArg, "\r\n");
@@ -1155,7 +1155,7 @@ void AddonChannelCommandHandler::SendFailed() // f Command failed, no body
}
// m Command message, message in body
-void AddonChannelCommandHandler::SendSysMessage(char const* str, bool escapeCharacters)
+void AddonChannelCommandHandler::SendSysMessage(std::string_view str, bool escapeCharacters)
{
ASSERT(echo);
if (!hadAck)
diff --git a/src/server/game/Chat/Chat.h b/src/server/game/Chat/Chat.h
index d46b58446c4..9aa32019ae2 100644
--- a/src/server/game/Chat/Chat.h
+++ b/src/server/game/Chat/Chat.h
@@ -49,7 +49,7 @@ class TC_GAME_API ChatHandler
// function with different implementation for chat/console
virtual char const* GetTrinityString(uint32 entry) const;
- virtual void SendSysMessage(char const* str, bool escapeCharacters = false);
+ virtual void SendSysMessage(std::string_view str, bool escapeCharacters = false);
void SendSysMessage(uint32 entry);
@@ -138,14 +138,14 @@ class TC_GAME_API ChatHandler
class TC_GAME_API CliHandler : public ChatHandler
{
public:
- typedef void Print(void*, char const*);
+ using Print = void(void*, std::string_view);
explicit CliHandler(void* callbackArg, Print* zprint) : m_callbackArg(callbackArg), m_print(zprint) { }
// overwrite functions
char const* GetTrinityString(uint32 entry) const override;
bool isAvailable(ChatCommand const& cmd) const override;
bool HasPermission(uint32 /*permission*/) const override { return true; }
- void SendSysMessage(const char *str, bool escapeCharacters) override;
+ void SendSysMessage(std::string_view, bool escapeCharacters) override;
bool ParseCommands(char const* str) override;
std::string GetNameLink() const override;
bool needReportToTarget(Player* chr) const override;
@@ -164,7 +164,7 @@ class TC_GAME_API AddonChannelCommandHandler : public ChatHandler
using ChatHandler::ChatHandler;
bool ParseCommands(char const* str) override;
- void SendSysMessage(char const* str, bool escapeCharacters) override;
+ void SendSysMessage(std::string_view, bool escapeCharacters) override;
using ChatHandler::SendSysMessage;
bool IsHumanReadable() const override { return humanReadable; }
diff --git a/src/server/game/Chat/ChatCommands/ChatCommand.h b/src/server/game/Chat/ChatCommands/ChatCommand.h
index 28d84629eea..7b36a72d2f4 100644
--- a/src/server/game/Chat/ChatCommands/ChatCommand.h
+++ b/src/server/game/Chat/ChatCommands/ChatCommand.h
@@ -25,77 +25,72 @@
#include "Errors.h"
#include "ObjectGuid.h"
#include "Optional.h"
+#include "StringFormat.h"
#include <cstddef>
#include <tuple>
#include <type_traits>
#include <vector>
class ChatHandler;
-class CommandArgs;
namespace Trinity::Impl::ChatCommands
{
- template <typename T>
- struct SingleConsumer
- {
- static Optional<std::string_view> TryConsumeTo(T& val, std::string_view args)
- {
- return ArgInfo<T>::TryConsume(val, args);
- }
- };
-
- /*
- for backwards compatibility, consumes the rest of the string
- new code should use the Tail/WTail tags defined in ChatCommandTags
- */
- template <>
- struct SingleConsumer<char const*>
- {
- static Optional<std::string_view> TryConsumeTo(char const*& arg, std::string_view args) { arg = args.data(); return std::string_view(); }
- };
-
-
// forward declaration
// ConsumeFromOffset contains the bounds check for offset, then hands off to MultiConsumer
// the call stack is MultiConsumer -> ConsumeFromOffset -> MultiConsumer -> ConsumeFromOffset etc
- // MultiConsumer calls SingleConsumer in each iteration
+ // MultiConsumer goes into ArgInfo for parsing on each iteration
template <typename Tuple, size_t offset>
- Optional<std::string_view> ConsumeFromOffset(Tuple&, std::string_view args);
+ ChatCommandResult ConsumeFromOffset(Tuple&, ChatHandler const* handler, std::string_view args);
template <typename Tuple, typename NextType, size_t offset>
struct MultiConsumer
{
- static Optional<std::string_view> TryConsumeTo(Tuple& tuple, std::string_view args)
+ static ChatCommandResult TryConsumeTo(Tuple& tuple, ChatHandler const* handler, std::string_view args)
{
- if (Optional<std::string_view> next = SingleConsumer<NextType>::TryConsumeTo(std::get<offset>(tuple), args))
- return ConsumeFromOffset<Tuple, offset + 1>(tuple, *next);
+ ChatCommandResult next = ArgInfo<NextType>::TryConsume(std::get<offset>(tuple), handler, args);
+ if (next)
+ return ConsumeFromOffset<Tuple, offset + 1>(tuple, handler, *next);
else
- return std::nullopt;
+ return next;
}
};
template <typename Tuple, typename NestedNextType, size_t offset>
struct MultiConsumer<Tuple, Optional<NestedNextType>, offset>
{
- static Optional<std::string_view> TryConsumeTo(Tuple& tuple, std::string_view args)
+ static ChatCommandResult TryConsumeTo(Tuple& tuple, ChatHandler const* handler, std::string_view args)
{
// try with the argument
auto& myArg = std::get<offset>(tuple);
myArg.emplace();
- if (Optional<std::string_view> next = SingleConsumer<NestedNextType>::TryConsumeTo(myArg.value(), args))
- if ((next = ConsumeFromOffset<Tuple, offset + 1>(tuple, *next)))
- return next;
+
+ 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;
- return ConsumeFromOffset<Tuple, offset + 1>(tuple, args);
+ ChatCommandResult result2 = ConsumeFromOffset<Tuple, offset + 1>(tuple, handler, args);
+ if (result2)
+ return result2;
+ if (result1.HasErrorMessage() && result2.HasErrorMessage())
+ {
+ return Trinity::StringFormat("%s \"%s\"\n%s \"%s\"",
+ GetTrinityString(handler, LANG_CMDPARSER_EITHER), result2.GetErrorMessage().c_str(),
+ GetTrinityString(handler, LANG_CMDPARSER_OR), result1.GetErrorMessage().c_str());
+ }
+ else if (result1.HasErrorMessage())
+ return result1;
+ else
+ return result2;
}
};
template <typename Tuple, size_t offset>
- Optional<std::string_view> ConsumeFromOffset(Tuple& tuple, std::string_view args)
+ 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, args);
+ 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
@@ -123,10 +118,15 @@ class TC_GAME_API ChatCommand
Tuple arguments;
std::get<0>(arguments) = chatHandler;
- if (Trinity::Impl::ChatCommands::ConsumeFromOffset<Tuple, 1>(arguments, argsStr))
+ Trinity::Impl::ChatCommands::ChatCommandResult result = Trinity::Impl::ChatCommands::ConsumeFromOffset<Tuple, 1>(arguments, chatHandler, argsStr);
+ if (result)
return std::apply(reinterpret_cast<TypedHandler>(handler), std::move(arguments));
else
+ {
+ if (result.HasErrorMessage())
+ Trinity::Impl::ChatCommands::SendErrorMessageToHandler(chatHandler, result.GetErrorMessage());
return false;
+ }
};
_handler = reinterpret_cast<void*>(handler);
}
diff --git a/src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp b/src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp
index 33db9d4bdf3..ca8fd888f1b 100644
--- a/src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp
+++ b/src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp
@@ -23,6 +23,7 @@
#include "Util.h"
using namespace Trinity::ChatCommands;
+using ChatCommandResult = Trinity::Impl::ChatCommands::ChatCommandResult;
struct AchievementVisitor
{
@@ -30,13 +31,14 @@ struct AchievementVisitor
value_type operator()(Hyperlink<achievement> achData) const { return achData->Achievement; }
value_type operator()(uint32 achId) const { return sAchievementStore.LookupEntry(achId); }
};
-Optional<std::string_view> Trinity::Impl::ChatCommands::ArgInfo<AchievementEntry const*>::TryConsume(AchievementEntry const*& data, std::string_view args)
+ChatCommandResult Trinity::Impl::ChatCommands::ArgInfo<AchievementEntry const*>::TryConsume(AchievementEntry const*& data, ChatHandler const* handler, std::string_view args)
{
Variant<Hyperlink<achievement>, uint32> val;
- Optional<std::string_view> next = SingleConsumer<decltype(val)>::TryConsumeTo(val, args);
- if (next)
- if ((data = val.visit(AchievementVisitor())))
- return next;
+ 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 FormatTrinityString(handler, LANG_CMDPARSER_ACHIEVEMENT_NO_EXIST, *id);
return std::nullopt;
}
@@ -46,13 +48,14 @@ struct CurrencyTypesVisitor
value_type operator()(Hyperlink<currency> currency) const { return currency->Currency; }
value_type operator()(uint32 currencyId) const { return sCurrencyTypesStore.LookupEntry(currencyId); }
};
-Optional<std::string_view> Trinity::Impl::ChatCommands::ArgInfo<CurrencyTypesEntry const*>::TryConsume(CurrencyTypesEntry const*& data, std::string_view args)
+ChatCommandResult Trinity::Impl::ChatCommands::ArgInfo<CurrencyTypesEntry const*>::TryConsume(CurrencyTypesEntry const*& data, ChatHandler const* handler, std::string_view args)
{
Variant<Hyperlink<currency>, uint32> val;
- Optional<std::string_view> next = SingleConsumer<decltype(val)>::TryConsumeTo(val, args);
- if (next)
- if ((data = val.visit(CurrencyTypesVisitor())))
- return next;
+ ChatCommandResult result = ArgInfo<decltype(val)>::TryConsume(val, handler, args);
+ if (!result || (data = val.visit(CurrencyTypesVisitor())))
+ return result;
+ if (uint32* id = std::get_if<uint32>(&val))
+ return FormatTrinityString(handler, LANG_CMDPARSER_CURRENCY_NO_EXIST, *id);
return std::nullopt;
}
@@ -60,16 +63,18 @@ struct GameTeleVisitor
{
using value_type = GameTele const*;
value_type operator()(Hyperlink<tele> tele) const { return sObjectMgr->GetGameTele(tele); }
- value_type operator()(std::string const& tele) const { return sObjectMgr->GetGameTele(tele); }
+ value_type operator()(std::string_view tele) const { return sObjectMgr->GetGameTele(tele); }
};
-Optional<std::string_view> Trinity::Impl::ChatCommands::ArgInfo<GameTele const*>::TryConsume(GameTele const*& data, std::string_view args)
+ChatCommandResult Trinity::Impl::ChatCommands::ArgInfo<GameTele const*>::TryConsume(GameTele const*& data, ChatHandler const* handler, std::string_view args)
{
- Variant<Hyperlink<tele>, std::string> val;
- Optional<std::string_view> next = SingleConsumer<decltype(val)>::TryConsumeTo(val, args);
- if (next)
- if ((data = val.visit(GameTeleVisitor())))
- return next;
- return std::nullopt;
+ 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 FormatTrinityString(handler, LANG_CMDPARSER_GAME_TELE_ID_NO_EXIST, static_cast<uint32>(std::get<Hyperlink<tele>>(val)));
+ else
+ return FormatTrinityString(handler, LANG_CMDPARSER_GAME_TELE_NO_EXIST, STRING_VIEW_FMT_ARG(std::get<std::string_view>(val)));
}
struct ItemTemplateVisitor
@@ -78,13 +83,14 @@ struct ItemTemplateVisitor
value_type operator()(Hyperlink<item> item) const { return item->Item; }
value_type operator()(uint32 item) const { return sObjectMgr->GetItemTemplate(item); }
};
-Optional<std::string_view> Trinity::Impl::ChatCommands::ArgInfo<ItemTemplate const*>::TryConsume(ItemTemplate const*& data, std::string_view args)
+ChatCommandResult Trinity::Impl::ChatCommands::ArgInfo<ItemTemplate const*>::TryConsume(ItemTemplate const*& data, ChatHandler const* handler, std::string_view args)
{
Variant<Hyperlink<item>, uint32> val;
- Optional<std::string_view> next = SingleConsumer<decltype(val)>::TryConsumeTo(val, args);
- if (next)
- if ((data = val.visit(ItemTemplateVisitor())))
- return next;
+ 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 FormatTrinityString(handler, LANG_CMDPARSER_ITEM_NO_EXIST, *id);
return std::nullopt;
}
@@ -103,12 +109,13 @@ struct SpellInfoVisitor
value_type operator()(uint32 spellId) const { return sSpellMgr->GetSpellInfo(spellId, DIFFICULTY_NONE); }
};
-Optional<std::string_view> Trinity::Impl::ChatCommands::ArgInfo<SpellInfo const*>::TryConsume(SpellInfo const*& data, std::string_view args)
+ChatCommandResult Trinity::Impl::ChatCommands::ArgInfo<SpellInfo const*>::TryConsume(SpellInfo const*& data, ChatHandler const* handler, std::string_view args)
{
Variant<Hyperlink<apower>, Hyperlink<conduit>, Hyperlink<enchant>, Hyperlink<mawpower>, Hyperlink<pvptal>, Hyperlink<spell>, Hyperlink<talent>, Hyperlink<trade>, uint32> val;
- Optional<std::string_view> next = SingleConsumer<decltype(val)>::TryConsumeTo(val, args);
- if (next)
- if ((data = val.visit(SpellInfoVisitor())))
- return next;
+ 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 FormatTrinityString(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
index 21f27b45752..b36456869d8 100644
--- a/src/server/game/Chat/ChatCommands/ChatCommandArgs.h
+++ b/src/server/game/Chat/ChatCommands/ChatCommandArgs.h
@@ -22,6 +22,7 @@
#include "ChatCommandTags.h"
#include "SmartEnum.h"
#include "StringConvert.h"
+#include "StringFormat.h"
#include "Util.h"
#include <charconv>
#include <map>
@@ -36,10 +37,13 @@ namespace Trinity::Impl::ChatCommands
/************************** ARGUMENT HANDLERS *******************************************\
|* Define how to extract contents of a certain requested type from a string *|
|* Must implement the following: *|
- |* - TryConsume: T&, std::string_view -> Optional<std::string_view> *|
- |* returns nullopt if no match, otherwise tail of argument string *|
- |* - if nullopt is returned, state of T& is indeterminate *|
- |* - otherwise, T& should be initialized to the intended return value *|
+ |* - 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>
@@ -49,7 +53,7 @@ namespace Trinity::Impl::ChatCommands
template <typename T>
struct ArgInfo<T, std::enable_if_t<std::is_integral_v<T> || std::is_floating_point_v<T>>>
{
- static Optional<std::string_view> TryConsume(T& val, std::string_view args)
+ static ChatCommandResult TryConsume(T& val, ChatHandler const* handler, std::string_view args)
{
auto [token, tail] = tokenize(args);
if (token.empty())
@@ -58,23 +62,33 @@ namespace Trinity::Impl::ChatCommands
if (Optional<T> v = StringTo<T>(token, 0))
val = *v;
else
- return std::nullopt;
+ return FormatTrinityString(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 std::nullopt;
+ return FormatTrinityString(handler, LANG_CMDPARSER_STRING_VALUE_INVALID, STRING_VIEW_FMT_ARG(token), GetTypeName<T>().c_str());
}
return tail;
}
};
+ /*
+ for backwards compatibility, consumes the rest of the string
+ new code should use the Tail/WTail tags defined in ChatCommandTags
+ */
+ template <>
+ struct ArgInfo<char const*, void>
+ {
+ static ChatCommandResult TryConsume(char const*& arg, ChatHandler const*, std::string_view args) { arg = args.data(); return std::string_view(); }
+ };
+
// string_view
template <>
struct ArgInfo<std::string_view, void>
{
- static Optional<std::string_view> TryConsume(std::string_view& val, std::string_view args)
+ static ChatCommandResult TryConsume(std::string_view& val, ChatHandler const*, std::string_view args)
{
auto [token, next] = tokenize(args);
if (token.empty())
@@ -88,10 +102,10 @@ namespace Trinity::Impl::ChatCommands
template <>
struct ArgInfo<std::string, void>
{
- static Optional<std::string_view> TryConsume(std::string& val, std::string_view args)
+ static ChatCommandResult TryConsume(std::string& val, ChatHandler const* handler, std::string_view args)
{
std::string_view view;
- Optional<std::string_view> next = ArgInfo<std::string_view>::TryConsume(view, args);
+ ChatCommandResult next = ArgInfo<std::string_view>::TryConsume(view, handler, args);
if (next)
val.assign(view);
return next;
@@ -102,13 +116,18 @@ namespace Trinity::Impl::ChatCommands
template <>
struct ArgInfo<std::wstring, void>
{
- static Optional<std::string_view> TryConsume(std::wstring& val, std::string_view args)
+ static ChatCommandResult TryConsume(std::wstring& val, ChatHandler const* handler, std::string_view args)
{
std::string_view utf8view;
- Optional<std::string_view> next = ArgInfo<std::string_view>::TryConsume(utf8view, args);
+ ChatCommandResult next = ArgInfo<std::string_view>::TryConsume(utf8view, handler, args);
- if (next && Utf8toWStr(utf8view, val))
- return next;
+ if (next)
+ {
+ if (Utf8toWStr(utf8view, val))
+ return next;
+ else
+ return GetTrinityString(handler, LANG_CMDPARSER_INVALID_UTF8);
+ }
else
return std::nullopt;
}
@@ -118,19 +137,18 @@ namespace Trinity::Impl::ChatCommands
template <typename T>
struct ArgInfo<T, std::enable_if_t<std::is_enum_v<T>>>
{
- static std::map<std::string, Optional<T>> MakeSearchMap()
+ using SearchMap = std::map<std::string_view, Optional<T>, StringCompareLessI_T>;
+ static SearchMap MakeSearchMap()
{
- std::map<std::string, Optional<T>> map;
+ SearchMap map;
for (T val : EnumUtils::Iterate<T>())
{
EnumText text = EnumUtils::ToString(val);
- std::string title(text.Title);
- strToLower(title);
- std::string constant(text.Constant);
- strToLower(constant);
+ std::string_view title(text.Title);
+ std::string_view constant(text.Constant);
- auto [constantIt, constantNew] = map.try_emplace(constant, val);
+ auto [constantIt, constantNew] = map.try_emplace(title, val);
if (!constantNew)
constantIt->second = std::nullopt;
@@ -144,21 +162,19 @@ namespace Trinity::Impl::ChatCommands
return map;
}
- static inline std::map<std::string, Optional<T>> const SearchMap = MakeSearchMap();
+ static inline SearchMap const _map = MakeSearchMap();
- static T const* Match(std::string s)
+ static T const* Match(std::string_view s)
{
- strToLower(s);
-
- auto it = SearchMap.lower_bound(s);
- if (it == SearchMap.end() || !StringStartsWith(it->first, s)) // not a match
+ auto it = _map.lower_bound(s);
+ if (it == _map.end() || !StringStartsWithI(it->first, s)) // not a match
return nullptr;
- if (it->first != s) // we don't have an exact match - check if it is unique
+ if (!StringEqualI(it->first, s)) // we don't have an exact match - check if it is unique
{
auto it2 = it;
++it2;
- if (it2 != SearchMap.end() && StringStartsWith(it2->first, s)) // not unique
+ if ((it2 != _map.end()) && StringStartsWithI(it2->first, s)) // not unique
return nullptr;
}
@@ -168,31 +184,35 @@ namespace Trinity::Impl::ChatCommands
return nullptr;
}
- static Optional<std::string_view> TryConsume(T& val, std::string_view args)
+ static ChatCommandResult TryConsume(T& val, ChatHandler const* handler, std::string_view args)
{
- std::string strVal;
- Optional<std::string_view> next = ArgInfo<std::string>::TryConsume(strVal, args);
-
- if (next)
+ 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 next;
+ 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;
- next = ArgInfo<U>::TryConsume(uVal, args);
- if (next && EnumUtils::IsValid<T>(uVal))
+ if (ChatCommandResult next2 = ArgInfo<U>::TryConsume(uVal, handler, args))
{
- val = static_cast<T>(uVal);
- return next;
+ if (EnumUtils::IsValid<T>(uVal))
+ {
+ val = static_cast<T>(uVal);
+ return next2;
+ }
}
- return std::nullopt;
+ if (next1)
+ return FormatTrinityString(handler, LANG_CMDPARSER_STRING_VALUE_INVALID, STRING_VIEW_FMT_ARG(strVal), GetTypeName<T>().c_str());
+ else
+ return next1;
}
};
@@ -200,9 +220,9 @@ namespace Trinity::Impl::ChatCommands
template <typename T>
struct ArgInfo<T, std::enable_if_t<std::is_base_of_v<ContainerTag, T>>>
{
- static Optional<std::string_view> TryConsume(T& tag, std::string_view args)
+ static ChatCommandResult TryConsume(T& tag, ChatHandler const* handler, std::string_view args)
{
- return tag.TryConsume(args);
+ return tag.TryConsume(handler, args);
}
};
@@ -210,16 +230,16 @@ namespace Trinity::Impl::ChatCommands
template <typename T>
struct ArgInfo<std::vector<T>, void>
{
- static Optional<std::string_view> TryConsume(std::vector<T>& val, std::string_view args)
+ static ChatCommandResult TryConsume(std::vector<T>& val, ChatHandler const* handler, std::string_view args)
{
val.clear();
- Optional<std::string_view> next = ArgInfo<T>::TryConsume(val.emplace_back(), args);
+ ChatCommandResult next = ArgInfo<T>::TryConsume(val.emplace_back(), handler, args);
if (!next)
- return std::nullopt;
+ return next;
- while (Optional<std::string_view> next2 = ArgInfo<T>::TryConsume(val.emplace_back(), *next))
- next = next2;
+ while (ChatCommandResult next2 = ArgInfo<T>::TryConsume(val.emplace_back(), handler, *next))
+ next = std::move(next2);
val.pop_back();
return next;
@@ -230,12 +250,12 @@ namespace Trinity::Impl::ChatCommands
template <typename T, size_t N>
struct ArgInfo<std::array<T, N>, void>
{
- static Optional<std::string_view> TryConsume(std::array<T, N>& val, std::string_view args)
+ static ChatCommandResult TryConsume(std::array<T, N>& val, ChatHandler const* handler, std::string_view args)
{
- Optional<std::string_view> next = args;
+ ChatCommandResult next = args;
for (T& t : val)
- if (!(next = ArgInfo<T>::TryConsume(t, *next)))
- return std::nullopt;
+ if (!(next = ArgInfo<T>::TryConsume(t, handler, *next)))
+ break;
return next;
}
};
@@ -248,22 +268,36 @@ namespace Trinity::Impl::ChatCommands
static constexpr size_t N = std::variant_size_v<V>;
template <size_t I>
- static Optional<std::string_view> TryAtIndex(Trinity::ChatCommands::Variant<Ts...>& val, [[maybe_unused]] std::string_view args)
+ static ChatCommandResult TryAtIndex([[maybe_unused]] Trinity::ChatCommands::Variant<Ts...>& val, [[maybe_unused]] ChatHandler const* handler, [[maybe_unused]] std::string_view args)
{
if constexpr (I < N)
{
- if (Optional<std::string_view> next = ArgInfo<std::variant_alternative_t<I, V>>::TryConsume(val.template emplace<I>(), args))
- return next;
+ ChatCommandResult thisResult = ArgInfo<std::variant_alternative_t<I, V>>::TryConsume(val.template emplace<I>(), handler, args);
+ if (thisResult)
+ return thisResult;
else
- return TryAtIndex<I + 1>(val, args);
+ {
+ ChatCommandResult nestedResult = TryAtIndex<I + 1>(val, handler, args);
+ if (!thisResult.HasErrorMessage())
+ return nestedResult;
+ if (!nestedResult.HasErrorMessage())
+ return thisResult;
+ if (StringStartsWith(nestedResult.GetErrorMessage(), "\""))
+ return Trinity::StringFormat("\"%s\"\n%s %s", thisResult.GetErrorMessage().c_str(), GetTrinityString(handler, LANG_CMDPARSER_OR), nestedResult.GetErrorMessage().c_str());
+ else
+ return Trinity::StringFormat("\"%s\"\n%s \"%s\"", thisResult.GetErrorMessage().c_str(), GetTrinityString(handler, LANG_CMDPARSER_OR), nestedResult.GetErrorMessage().c_str());
+ }
}
else
return std::nullopt;
}
- static Optional<std::string_view> TryConsume(Trinity::ChatCommands::Variant<Ts...>& val, std::string_view args)
+ static ChatCommandResult TryConsume(Trinity::ChatCommands::Variant<Ts...>& val, ChatHandler const* handler, std::string_view args)
{
- return TryAtIndex<0>(val, args);
+ ChatCommandResult result = TryAtIndex<0>(val, handler, args);
+ if (result.HasErrorMessage() && (result.GetErrorMessage().find('\n') != std::string::npos))
+ return Trinity::StringFormat("%s %s", GetTrinityString(handler, LANG_CMDPARSER_EITHER), result.GetErrorMessage().c_str());
+ return result;
}
};
@@ -271,35 +305,35 @@ namespace Trinity::Impl::ChatCommands
template <>
struct TC_GAME_API ArgInfo<AchievementEntry const*>
{
- static Optional<std::string_view> TryConsume(AchievementEntry const*&, std::string_view);
+ static ChatCommandResult TryConsume(AchievementEntry const*&, ChatHandler const*, std::string_view);
};
// CurrencyTypesEntry* from numeric id or link
template <>
struct TC_GAME_API ArgInfo<CurrencyTypesEntry const*>
{
- static Optional<std::string_view> TryConsume(CurrencyTypesEntry const*&, std::string_view);
+ static ChatCommandResult TryConsume(CurrencyTypesEntry const*&, ChatHandler const*, std::string_view);
};
// GameTele* from string name or link
template <>
struct TC_GAME_API ArgInfo<GameTele const*>
{
- static Optional<std::string_view> TryConsume(GameTele const*&, std::string_view);
+ static ChatCommandResult TryConsume(GameTele const*&, ChatHandler const*, std::string_view);
};
// ItemTemplate* from numeric id or link
template <>
struct TC_GAME_API ArgInfo<ItemTemplate const*>
{
- static Optional<std::string_view> TryConsume(ItemTemplate const*&, std::string_view);
+ static ChatCommandResult TryConsume(ItemTemplate const*&, ChatHandler const*, std::string_view);
};
// SpellInfo const* from spell id or link
template <>
struct TC_GAME_API ArgInfo<SpellInfo const*>
{
- static Optional<std::string_view> TryConsume(SpellInfo const*&, std::string_view);
+ static ChatCommandResult TryConsume(SpellInfo const*&, ChatHandler const*, std::string_view);
};
}
diff --git a/src/server/game/Chat/ChatCommands/ChatCommandHelpers.cpp b/src/server/game/Chat/ChatCommands/ChatCommandHelpers.cpp
new file mode 100644
index 00000000000..ab64584a389
--- /dev/null
+++ b/src/server/game/Chat/ChatCommands/ChatCommandHelpers.cpp
@@ -0,0 +1,30 @@
+/*
+ * This file is part of the TrinityCore 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 General Public License as published by the
+ * Free Software Foundation; either version 2 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 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"
+
+void Trinity::Impl::ChatCommands::SendErrorMessageToHandler(ChatHandler* handler, std::string_view str)
+{
+ handler->SendSysMessage(str);
+ handler->SetSentErrorMessage(true);
+}
+
+char const* Trinity::Impl::ChatCommands::GetTrinityString(ChatHandler const* handler, TrinityStrings which)
+{
+ return handler->GetTrinityString(which);
+}
diff --git a/src/server/game/Chat/ChatCommands/ChatCommandHelpers.h b/src/server/game/Chat/ChatCommands/ChatCommandHelpers.h
index 712d0c37d48..afa5589e0b7 100644
--- a/src/server/game/Chat/ChatCommands/ChatCommandHelpers.h
+++ b/src/server/game/Chat/ChatCommands/ChatCommandHelpers.h
@@ -18,8 +18,16 @@
#ifndef TRINITY_CHATCOMMANDHELPERS_H
#define TRINITY_CHATCOMMANDHELPERS_H
+#include "Define.h"
+#include "Language.h"
+#include "Optional.h"
+#include "StringFormat.h"
+#include <string>
#include <string_view>
#include <type_traits>
+#include <variant>
+
+class ChatHandler;
namespace Trinity::Impl::ChatCommands
{
@@ -82,6 +90,43 @@ namespace Trinity::Impl::ChatCommands
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;
+ };
+
+ TC_GAME_API void SendErrorMessageToHandler(ChatHandler* handler, std::string_view str);
+ TC_GAME_API char const* GetTrinityString(ChatHandler const* handler, TrinityStrings which);
+ template <typename... Ts>
+ std::string FormatTrinityString(ChatHandler const* handler, TrinityStrings which, Ts&&... args)
+ {
+ return Trinity::StringFormat(GetTrinityString(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
index 8c5917e086d..b5b29193312 100644
--- a/src/server/game/Chat/ChatCommands/ChatCommandTags.cpp
+++ b/src/server/game/Chat/ChatCommands/ChatCommandTags.cpp
@@ -25,19 +25,21 @@
#include "ObjectMgr.h"
#include "Player.h"
-Optional<std::string_view> Trinity::ChatCommands::QuotedString::TryConsume(std::string_view args)
+using namespace Trinity::Impl::ChatCommands;
+
+ChatCommandResult Trinity::ChatCommands::QuotedString::TryConsume(ChatHandler const* handler, std::string_view args)
{
if (args.empty())
return std::nullopt;
if ((args[0] != '"') && (args[0] != '\''))
- return Trinity::Impl::ChatCommands::ArgInfo<std::string>::TryConsume(*this, args);
+ 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] = Trinity::Impl::ChatCommands::tokenize(args.substr(i + 1));
+ 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
@@ -56,17 +58,17 @@ Optional<std::string_view> Trinity::ChatCommands::QuotedString::TryConsume(std::
return std::nullopt;
}
-Optional<std::string_view> Trinity::ChatCommands::AccountIdentifier::TryConsume(std::string_view args)
+ChatCommandResult Trinity::ChatCommands::AccountIdentifier::TryConsume(ChatHandler const* handler, std::string_view args)
{
std::string_view text;
- Optional<std::string_view> next = Trinity::Impl::ChatCommands::ArgInfo<std::string_view>::TryConsume(text, args);
+ ChatCommandResult next = ArgInfo<std::string_view>::TryConsume(text, handler, args);
if (!next)
- return std::nullopt;
+ return next;
// first try parsing as account name
_name.assign(text);
if (!Utf8ToUpperOnlyLatin(_name))
- return std::nullopt;
+ return GetTrinityString(handler, LANG_CMDPARSER_INVALID_UTF8);
_id = AccountMgr::GetId(_name);
if (_id) // account with name exists, we are done
return next;
@@ -74,21 +76,21 @@ Optional<std::string_view> Trinity::ChatCommands::AccountIdentifier::TryConsume(
// try parsing as account id instead
Optional<uint32> id = Trinity::StringTo<uint32>(text, 10);
if (!id)
- return std::nullopt;
+ return FormatTrinityString(handler, LANG_CMDPARSER_ACCOUNT_NAME_NO_EXIST, STRING_VIEW_FMT_ARG(_name));
_id = *id;
if (AccountMgr::GetName(_id, _name))
return next;
else
- return std::nullopt;
+ return FormatTrinityString(handler, LANG_CMDPARSER_ACCOUNT_ID_NO_EXIST, _id);
}
-Optional<std::string_view> Trinity::ChatCommands::PlayerIdentifier::TryConsume(std::string_view args)
+ChatCommandResult Trinity::ChatCommands::PlayerIdentifier::TryConsume(ChatHandler const* handler, std::string_view args)
{
Variant<Hyperlink<player>, ObjectGuid::LowType, std::string_view> val;
- Optional<std::string_view> next = Trinity::Impl::ChatCommands::ArgInfo<decltype(val)>::TryConsume(val, args);
+ ChatCommandResult next = ArgInfo<decltype(val)>::TryConsume(val, handler, args);
if (!next)
- return std::nullopt;
+ return next;
if (val.holds_alternative<ObjectGuid::LowType>())
{
@@ -96,7 +98,7 @@ Optional<std::string_view> Trinity::ChatCommands::PlayerIdentifier::TryConsume(s
if ((_player = ObjectAccessor::FindPlayerByLowGUID(_guid.GetCounter())))
_name = _player->GetName();
else if (!sCharacterCache->GetCharacterNameByGuid(_guid, _name))
- return std::nullopt;
+ return FormatTrinityString(handler, LANG_CMDPARSER_CHAR_GUID_NO_EXIST, _guid.ToString().c_str());
return next;
}
else
@@ -107,12 +109,12 @@ Optional<std::string_view> Trinity::ChatCommands::PlayerIdentifier::TryConsume(s
_name.assign(val.get<std::string_view>());
if (!normalizePlayerName(_name))
- return std::nullopt;
+ return FormatTrinityString(handler, LANG_CMDPARSER_CHAR_NAME_INVALID, STRING_VIEW_FMT_ARG(_name));
if ((_player = ObjectAccessor::FindPlayerByName(_name)))
_guid = _player->GetGUID();
else if (!(_guid = sCharacterCache->GetCharacterGuidByName(_name)))
- return std::nullopt;
+ return FormatTrinityString(handler, LANG_CMDPARSER_CHAR_NAME_NO_EXIST, STRING_VIEW_FMT_ARG(_name));
return next;
}
}
diff --git a/src/server/game/Chat/ChatCommands/ChatCommandTags.h b/src/server/game/Chat/ChatCommands/ChatCommandTags.h
index 879d1559dfa..dca28d205e8 100644
--- a/src/server/game/Chat/ChatCommands/ChatCommandTags.h
+++ b/src/server/game/Chat/ChatCommands/ChatCommandTags.h
@@ -41,7 +41,11 @@ class Player;
namespace Trinity::Impl::ChatCommands
{
- struct ContainerTag {};
+ struct ContainerTag
+ {
+ using ChatCommandResult = Trinity::Impl::ChatCommands::ChatCommandResult;
+ };
+
template <typename T>
struct tag_base<T, std::enable_if_t<std::is_base_of_v<ContainerTag, T>>>
{
@@ -71,8 +75,11 @@ namespace Trinity::ChatCommands
|* Simple holder classes to differentiate between extraction methods *|
|* Must inherit from Trinity::Impl::ChatCommands::ContainerTag *|
|* Must implement the following: *|
- |* - TryConsume: std::string_view -> Optional<std::string_view> *|
- |* returns nullopt if no match, otherwise the tail of the provided argument string *|
+ |* - 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 *|
|* *|
@@ -83,15 +90,17 @@ namespace Trinity::ChatCommands
{
using value_type = void;
- Optional<std::string_view> TryConsume(std::string_view args) const
+ ChatCommandResult TryConsume(ChatHandler const* handler, std::string_view args) const
{
- if (StringStartsWithI(args, _string))
+ std::string_view start = args.substr(0, _string.length());
+ if (StringEqualI(start, _string))
{
auto [remainingToken, tail] = Trinity::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 std::nullopt;
+ return Trinity::Impl::ChatCommands::FormatTrinityString(handler, LANG_CMDPARSER_EXACT_SEQ_MISMATCH, STRING_VIEW_FMT_ARG(_string), STRING_VIEW_FMT_ARG(start));
}
private:
@@ -108,7 +117,7 @@ namespace Trinity::ChatCommands
using std::string_view::operator=;
- Optional<std::string_view> TryConsume(std::string_view args)
+ ChatCommandResult TryConsume(ChatHandler const*,std::string_view args)
{
std::string_view::operator=(args);
return std::string_view();
@@ -121,12 +130,12 @@ namespace Trinity::ChatCommands
using std::wstring::operator=;
- Optional<std::string_view> TryConsume(std::string_view args)
+ ChatCommandResult TryConsume(ChatHandler const* handler, std::string_view args)
{
if (Utf8toWStr(args, *this))
return std::string_view();
else
- return std::nullopt;
+ return Trinity::Impl::ChatCommands::GetTrinityString(handler, LANG_CMDPARSER_INVALID_UTF8);
}
};
@@ -134,7 +143,7 @@ namespace Trinity::ChatCommands
{
using value_type = std::string;
- TC_GAME_API Optional<std::string_view> TryConsume(std::string_view args);
+ TC_GAME_API ChatCommandResult TryConsume(ChatHandler const* handler, std::string_view args);
};
struct TC_GAME_API AccountIdentifier : Trinity::Impl::ChatCommands::ContainerTag
@@ -148,7 +157,7 @@ namespace Trinity::ChatCommands
uint32 GetID() const { return _id; }
std::string const& GetName() const { return _name; }
- Optional<std::string_view> TryConsume(std::string_view args);
+ ChatCommandResult TryConsume(ChatHandler const* handler, std::string_view args);
private:
uint32 _id;
@@ -171,7 +180,7 @@ namespace Trinity::ChatCommands
bool IsConnected() const { return (_player != nullptr); }
Player* GetConnectedPlayer() const { return _player; }
- Optional<std::string_view> TryConsume(std::string_view args);
+ ChatCommandResult TryConsume(ChatHandler const* handler, std::string_view args);
static Optional<PlayerIdentifier> FromTarget(ChatHandler* handler);
static Optional<PlayerIdentifier> FromSelf(ChatHandler* handler);
@@ -199,7 +208,7 @@ namespace Trinity::ChatCommands
value_type operator*() const { return val; }
storage_type const* operator->() const { return &val; }
- Optional<std::string_view> TryConsume(std::string_view args)
+ ChatCommandResult TryConsume(ChatHandler const* handler, std::string_view args)
{
Trinity::Hyperlinks::HyperlinkInfo info = Trinity::Hyperlinks::ParseSingleHyperlink(args);
// invalid hyperlinks cannot be consumed
@@ -212,7 +221,7 @@ namespace Trinity::ChatCommands
// store value
if (!linktag::StoreTo(val, info.data))
- return std::nullopt;
+ return Trinity::Impl::ChatCommands::GetTrinityString(handler, LANG_CMDPARSER_LINKDATA_INVALID);
// finally, skip any potential delimiters
auto [token, next] = Trinity::Impl::ChatCommands::tokenize(info.tail);
diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h
index eb3cfb44ec9..99c4342a3bb 100644
--- a/src/server/game/Miscellaneous/Language.h
+++ b/src/server/game/Miscellaneous/Language.h
@@ -994,22 +994,23 @@ enum TrinityStrings
// 1219-1499 - free
// Command argument parsers
- LANG_CMDPARSER_EITHER = 1500, // 3.3.5 RESERVED
- LANG_CMDPARSER_OR = 1501, // 3.3.5 RESERVED
- LANG_CMDPARSER_STRING_VALUE_INVALID = 1502, // 3.3.5 RESERVED
- LANG_CMDPARSER_INVALID_UTF8 = 1503, // 3.3.5 RESERVED
- LANG_CMDPARSER_LINKDATA_INVALID = 1504, // 3.3.5 RESERVED
- LANG_CMDPARSER_ACCOUNT_NAME_NO_EXIST = 1505, // 3.3.5 RESERVED
- LANG_CMDPARSER_ACCOUNT_ID_NO_EXIST = 1506, // 3.3.5 RESERVED
- LANG_CMDPARSER_CHAR_GUID_NO_EXIST = 1507, // 3.3.5 RESERVED
- LANG_CMDPARSER_CHAR_NAME_NO_EXIST = 1508, // 3.3.5 RESERVED
- LANG_CMDPARSER_CHAR_NAME_INVALID = 1509, // 3.3.5 RESERVED
- LANG_CMDPARSER_ACHIEVEMENT_NO_EXIST = 1510, // 3.3.5 RESERVED
- LANG_CMDPARSER_GAME_TELE_ID_NO_EXIST = 1511, // 3.3.5 RESERVED
- LANG_CMDPARSER_GAME_TELE_NO_EXIST = 1512, // 3.3.5 RESERVED
- LANG_CMDPARSER_ITEM_NO_EXIST = 1513, // 3.3.5 RESERVED
- LANG_CMDPARSER_SPELL_NO_EXIST = 1514, // 3.3.5 RESERVED
- LANG_CMDPARSER_EXACT_SEQ_MISMATCH = 1515, // 3.3.5 RESERVED
+ 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,
+ LANG_CMDPARSER_CURRENCY_NO_EXIST = 1516,
// 1516-1998 - free
LANG_DEBUG_AREATRIGGER_LEFT = 1999,
diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h
index 8d1efd74f0e..f3d18a0b989 100644
--- a/src/server/game/World/World.h
+++ b/src/server/game/World/World.h
@@ -562,8 +562,8 @@ enum RealmZone
/// Storage class for commands issued for delayed execution
struct TC_GAME_API CliCommandHolder
{
- typedef void(*Print)(void*, char const*);
- 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;
diff --git a/src/server/scripts/Commands/cs_gobject.cpp b/src/server/scripts/Commands/cs_gobject.cpp
index f87498b76c5..34e8cae35a4 100644
--- a/src/server/scripts/Commands/cs_gobject.cpp
+++ b/src/server/scripts/Commands/cs_gobject.cpp
@@ -41,9 +41,7 @@ EndScriptData */
#include "PoolMgr.h"
#include "RBAC.h"
#include "WorldSession.h"
-#include <boost/core/demangle.hpp>
#include <sstream>
-#include <typeinfo>
using namespace Trinity::ChatCommands;
@@ -574,7 +572,7 @@ public:
handler->PSendSysMessage(LANG_GOINFO_SIZE, gameObjectInfo->size);
handler->PSendSysMessage(LANG_OBJECTINFO_AIINFO, gameObjectInfo->AIName.c_str(), sObjectMgr->GetScriptName(gameObjectInfo->ScriptId).c_str());
if (GameObjectAI const* ai = thisGO ? thisGO->AI() : nullptr)
- handler->PSendSysMessage(LANG_OBJECTINFO_AITYPE, boost::core::demangle(typeid(*ai).name()).c_str());
+ handler->PSendSysMessage(LANG_OBJECTINFO_AITYPE, GetTypeName(*ai).c_str());
if (GameObjectDisplayInfoEntry const* modelInfo = sGameObjectDisplayInfoStore.LookupEntry(displayId))
handler->PSendSysMessage(LANG_GOINFO_MODEL, modelInfo->GeoBoxMax.X, modelInfo->GeoBoxMax.Y, modelInfo->GeoBoxMax.Z, modelInfo->GeoBoxMin.X, modelInfo->GeoBoxMin.Y, modelInfo->GeoBoxMin.Z);
diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp
index 6112f51c7a9..b2ad46337ef 100644
--- a/src/server/scripts/Commands/cs_npc.cpp
+++ b/src/server/scripts/Commands/cs_npc.cpp
@@ -45,8 +45,6 @@ EndScriptData */
#include "Transport.h"
#include "World.h"
#include "WorldSession.h"
-#include <boost/core/demangle.hpp>
-#include <typeinfo>
using namespace Trinity::ChatCommands;
using CreatureSpawnId = Variant<Hyperlink<creature>, ObjectGuid::LowType>;
@@ -550,7 +548,7 @@ public:
handler->PSendSysMessage(LANG_OBJECTINFO_AIINFO, target->GetAIName().c_str(), target->GetScriptName().c_str());
handler->PSendSysMessage(LANG_NPCINFO_REACTSTATE, DescribeReactState(target->GetReactState()));
if (CreatureAI const* ai = target->AI())
- handler->PSendSysMessage(LANG_OBJECTINFO_AITYPE, boost::core::demangle(typeid(*ai).name()).c_str());
+ handler->PSendSysMessage(LANG_OBJECTINFO_AITYPE, GetTypeName(*ai).c_str());
handler->PSendSysMessage(LANG_NPCINFO_FLAGS_EXTRA, cInfo->flags_extra);
for (CreatureFlagsExtra flag : EnumUtils::Iterate<CreatureFlagsExtra>())
if (cInfo->flags_extra & flag)
diff --git a/src/server/worldserver/CommandLine/CliRunnable.cpp b/src/server/worldserver/CommandLine/CliRunnable.cpp
index 29981235cfb..b36e9edf9d1 100644
--- a/src/server/worldserver/CommandLine/CliRunnable.cpp
+++ b/src/server/worldserver/CommandLine/CliRunnable.cpp
@@ -97,18 +97,17 @@ int cli_hook_func()
#endif
-void utf8print(void* /*arg*/, char const* str)
+void utf8print(void* /*arg*/, std::string_view str)
{
#if TRINITY_PLATFORM == TRINITY_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;
- wprintf(L"%s", wtemp_buf);
+ wprintf(L"%s", wbuf.c_str());
#else
{
- printf("%s", str);
+ printf(STRING_VIEW_FMT, STRING_VIEW_FMT_ARG(str));
fflush(stdout);
}
#endif
diff --git a/src/server/worldserver/RemoteAccess/RASession.cpp b/src/server/worldserver/RemoteAccess/RASession.cpp
index 1cd6bbfe01b..8e32177fdc4 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(char const* data)
+int RASession::Send(std::string_view data)
{
std::ostream os(&_writeBuffer);
os << data;
@@ -204,9 +204,9 @@ bool RASession::ProcessCommand(std::string& command)
return false;
}
-void RASession::CommandPrint(void* callbackArg, char const* text)
+void RASession::CommandPrint(void* callbackArg, std::string_view text)
{
- if (!text || !*text)
+ if (text.empty())
return;
RASession* session = static_cast<RASession*>(callbackArg);
diff --git a/src/server/worldserver/RemoteAccess/RASession.h b/src/server/worldserver/RemoteAccess/RASession.h
index e775bed5e33..d499727ae9d 100644
--- a/src/server/worldserver/RemoteAccess/RASession.h
+++ b/src/server/worldserver/RemoteAccess/RASession.h
@@ -42,13 +42,13 @@ public:
unsigned short GetRemotePort() const { return _socket.remote_endpoint().port(); }
private:
- int Send(char const* 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, char const* 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/TCSoap/TCSoap.h b/src/server/worldserver/TCSoap/TCSoap.h
index e45d044983b..be680eb4ba2 100644
--- a/src/server/worldserver/TCSoap/TCSoap.h
+++ b/src/server/worldserver/TCSoap/TCSoap.h
@@ -38,7 +38,7 @@ class SOAPCommand
{
}
- void appendToPrintBuffer(char const* msg)
+ void appendToPrintBuffer(std::string_view msg)
{
m_printBuffer += msg;
}
@@ -54,7 +54,7 @@ class SOAPCommand
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/tests/game/ChatCommand.cpp b/tests/game/ChatCommand.cpp
index 2f73b579f84..af5cfd783ad 100644
--- a/tests/game/ChatCommand.cpp
+++ b/tests/game/ChatCommand.cpp
@@ -17,15 +17,24 @@
#include "tc_catch2.h"
+#include "Chat.h"
#include "ChatCommand.h"
using namespace Trinity::ChatCommands;
using namespace std::string_view_literals;
+struct DummyChatHandler : ChatHandler
+{
+ DummyChatHandler() : ChatHandler(nullptr) {}
+ void SendSysMessage(std::string_view, bool) override {}
+ char const* GetTrinityString(uint32) const override { return ""; }
+};
+
template <typename F>
static void TestChatCommand(char const* c, F f, Optional<bool> expected = true)
{
- bool r = ChatCommand("", 0, false, +f, "")(nullptr, c);
+ DummyChatHandler handler;
+ bool r = ChatCommand("", 0, false, +f, "")(&handler, c);
if (expected)
REQUIRE(r == *expected);
}