[3.3.5] Core/ChatCommands: Show error messages from argument parsers (PR #25443)

(cherry picked from commit 75f9e7396e35360f3016cc0cb21e72e20f5d96d5)
This commit is contained in:
Treeston
2022-02-05 23:42:15 +01:00
committed by Shauren
parent 7d0586812a
commit 378691aaa2
22 changed files with 381 additions and 206 deletions
@@ -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.');
+3
View File
@@ -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;
+14 -7
View File
@@ -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)
+13
View File
@@ -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
{
+3 -3
View File
@@ -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)
+4 -4
View File
@@ -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; }
+35 -35
View File
@@ -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))
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);
};
}
@@ -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);
+17 -16
View File
@@ -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,
+2 -2
View File
@@ -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;
+1 -3
View File
@@ -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);
+1 -3
View File
@@ -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;
+2 -2
View File
@@ -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);
}
+10 -1
View File
@@ -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);
}