[3.3.5] Core/ChatCommands: Show error messages from argument parsers (PR #25443)
(cherry picked from commit 75f9e7396e35360f3016cc0cb21e72e20f5d96d5)
This commit is contained in:
@@ -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.');
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
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))
|
||||
{
|
||||
if (EnumUtils::IsValid<T>(uVal))
|
||||
{
|
||||
val = static_cast<T>(uVal);
|
||||
return next;
|
||||
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);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user