aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/server/game/Chat/ChatCommands/ChatCommand.h259
-rw-r--r--src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp43
-rw-r--r--src/server/game/Chat/ChatCommands/ChatCommandArgs.h154
-rw-r--r--src/server/game/Chat/ChatCommands/ChatCommandHelpers.h36
-rw-r--r--src/server/game/Chat/ChatCommands/ChatCommandTags.h112
-rw-r--r--src/server/game/Chat/HyperlinkTags.cpp159
-rw-r--r--src/server/game/Chat/Hyperlinks.cpp292
-rw-r--r--src/server/game/Chat/Hyperlinks.h145
-rw-r--r--src/server/scripts/Commands/cs_debug.cpp4
-rw-r--r--src/server/scripts/Commands/cs_message.cpp38
-rw-r--r--tests/game/ChatCommand.cpp58
11 files changed, 655 insertions, 645 deletions
diff --git a/src/server/game/Chat/ChatCommands/ChatCommand.h b/src/server/game/Chat/ChatCommands/ChatCommand.h
index c54f7f52cb5..dac914485d0 100644
--- a/src/server/game/Chat/ChatCommands/ChatCommand.h
+++ b/src/server/game/Chat/ChatCommands/ChatCommand.h
@@ -33,199 +33,106 @@
class ChatHandler;
class CommandArgs;
-template <typename T>
-struct CommandArgsConsumerSingle
+namespace Trinity::Impl::ChatCommands
{
- using arginfo = Trinity::ChatCommands::ArgInfo<T>;
- static char const* TryConsumeTo(T& val, char const* args)
+ template <typename T>
+ struct SingleConsumer
{
- return arginfo::TryConsume(val, args);
- }
-};
-
-struct CommandArgsVariantConsumer
-{
- template <typename V, typename T1, typename... Ts>
- static char const* TryConsumeTo(V& val, char const* args)
- {
- T1 v;
- if (char const* next = CommandArgsConsumerSingle<T1>::TryConsumeTo(v, args))
+ static Optional<std::string_view> TryConsumeTo(T& val, std::string_view args)
{
- val = std::move(v);
- return next;
+ return ArgInfo<T>::TryConsume(val, args);
}
- else if constexpr (sizeof...(Ts) > 0)
- return TryConsumeTo<V, Ts...>(val, args);
- else
- return nullptr;
- }
-};
-
-template <typename... Ts>
-struct CommandArgsConsumerSingle<Trinity::ChatCommands::Variant<Ts...>>
-{
- static char const* TryConsumeTo(Trinity::ChatCommands::Variant<Ts...>& val, char const* args)
- {
- return CommandArgsVariantConsumer::TryConsumeTo<Trinity::ChatCommands::Variant<Ts...>, Ts...>(val, args);
- }
-};
+ };
-template <typename T>
-struct CommandArgsConsumerSingle<std::vector<T>>
-{
- static char const* TryConsumeTo(std::vector<T>& val, char const* args)
+ template <typename... Ts>
+ struct SingleConsumer<Trinity::ChatCommands::Variant<Ts...>>
{
- char const* last;
- val.clear();
-
- do val.emplace_back();
- while ((args = CommandArgsConsumerSingle<T>::TryConsumeTo(val.back(), (last = args))));
-
- val.pop_back();
- return last;
- }
-};
+ using V = std::variant<Ts...>;
+ static constexpr size_t N = std::variant_size_v<V>;
-template <typename T, std::size_t C>
-struct CommandArgsConsumerSingle<std::array<T, C>>
-{
- static char const* TryConsumeTo(std::array<T, C>& val, char const* args)
- {
- for (T& t : val)
+ template <size_t I>
+ static Optional<std::string_view> TryAtIndex(Trinity::ChatCommands::Variant<Ts...>& val, [[maybe_unused]] std::string_view args)
{
- args = CommandArgsConsumerSingle<T>::TryConsumeTo(t, args);
-
- if (!args)
- return nullptr;
+ 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;
+ else
+ return TryAtIndex<I+1>(val, args);
+ }
+ else
+ return std::nullopt;
}
- return args;
- }
-};
-
-template <>
-struct CommandArgsConsumerSingle<CommandArgs*>
-{
- static char const* TryConsumeTo(CommandArgs*&, char const* args) { return args; }
-};
-
-template <>
-struct CommandArgsConsumerSingle<char const*>
-{
- static char const* TryConsumeTo(char const*&, char const* args) { return args; }
-};
-
-template <typename T, size_t offset>
-struct CommandArgsConsumerNext;
-
-template <typename Tuple, typename NextType, size_t offset>
-struct CommandArgsConsumerMulti
-{
- static char const* TryConsumeTo(Tuple& tuple, char const* args)
- {
- if (char const* next = CommandArgsConsumerSingle<NextType>::TryConsumeTo(std::get<offset>(tuple), args))
- return CommandArgsConsumerNext<Tuple, offset+1>::GoNext(tuple, next);
- else
- return nullptr;
- }
-};
-
-template <typename Tuple, typename NestedNextType, size_t offset>
-struct CommandArgsConsumerMulti<Tuple, Optional<NestedNextType>, offset>
-{
- static char const* TryConsumeTo(Tuple& tuple, char const* args)
+ static Optional<std::string_view> TryConsumeTo(Trinity::ChatCommands::Variant<Ts...>& val, std::string_view args)
+ {
+ return TryAtIndex<0>(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*>
{
- // try with the argument
- auto& myArg = std::get<offset>(tuple);
- myArg.emplace();
- if (char const* next = CommandArgsConsumerSingle<NestedNextType>::TryConsumeTo(myArg.value(), args))
- if ((next = CommandArgsConsumerNext<Tuple, offset+1>::GoNext(tuple, next)))
- return next;
- // try again omitting the argument
- myArg = std::nullopt;
- if (char const* next = CommandArgsConsumerNext<Tuple, offset+1>::GoNext(tuple, args))
- return next;
- return nullptr;
- }
-};
+ static Optional<std::string_view> TryConsumeTo(char const*& arg, std::string_view args) { arg = args.data(); return std::string_view(); }
+ };
-template <size_t offset, typename... Ts>
-struct CommandArgsConsumerNext<std::tuple<Ts...>, offset>
-{
- using tuple_type = std::tuple<Ts...>;
- template <bool C = (offset < sizeof...(Ts))>
- static std::enable_if_t<C, char const*> GoNext(tuple_type& tuple, char const* args)
- {
- return CommandArgsConsumerMulti<tuple_type, std::tuple_element_t<offset, tuple_type>, offset>::TryConsumeTo(tuple, args);
- }
+ // 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
+ template <typename Tuple, size_t offset>
+ Optional<std::string_view> ConsumeFromOffset(Tuple&, std::string_view args);
- template <bool C = (offset < sizeof...(Ts))>
- static std::enable_if_t<!C, char const*> GoNext(tuple_type&, char const* args)
+ template <typename Tuple, typename NextType, size_t offset>
+ struct MultiConsumer
{
- return args;
- }
-};
-
-class TC_GAME_API CommandArgs
-{
- public:
- CommandArgs(char const* args) : _original(args), _args(args) {}
-
- template <typename T1, typename T2, typename... Ts>
- auto TryConsume()
+ static Optional<std::string_view> TryConsumeTo(Tuple& tuple, std::string_view args)
{
- Optional<std::tuple<advstd::remove_cvref_t<T1>, advstd::remove_cvref_t<T2>, advstd::remove_cvref_t<Ts>...>> rv;
- rv.emplace();
- if (!TryConsumeToTuple<0>(rv.value()))
- rv = std::nullopt;
- return rv;
- }
-
- template <typename T1>
- auto TryConsume()
- {
- using T = advstd::remove_cvref_t<T1>;
- Optional<T> rv;
- rv.emplace();
- if (char const* next = CommandArgsConsumerSingle<T>::TryConsumeTo(rv.value(), _args))
- _args = next;
+ if (Optional<std::string_view> next = SingleConsumer<NextType>::TryConsumeTo(std::get<offset>(tuple), args))
+ return ConsumeFromOffset<Tuple, offset + 1>(tuple, *next);
else
- rv = std::nullopt;
- return rv;
+ return std::nullopt;
}
+ };
- template <size_t offset = 0, typename T>
- bool TryConsumeToTuple(T& tuple)
+ 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)
{
- if (char const* next = CommandArgsConsumerNext<T, offset>::GoNext(tuple, _args))
- {
- _args = next;
- return true;
- }
- else
- return false;
+ // 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;
+ // try again omitting the argument
+ myArg = std::nullopt;
+ return ConsumeFromOffset<Tuple, offset + 1>(tuple, args);
}
+ };
- void Reset() { _args = _original; }
-
- char const* GetFullArgs() const { return _original; }
- char const* GetRemainingArgs() const { return _args; }
-
- bool IsEmpty() const { return !!*_args; }
- explicit operator bool() const { return IsEmpty(); }
-
- private:
- char const* const _original;
- char const* _args;
-};
+ template <typename Tuple, size_t offset>
+ Optional<std::string_view> ConsumeFromOffset(Tuple& tuple, 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);
+ else if (!args.empty()) /* the entire string must be consumed */
+ return std::nullopt;
+ else
+ return args;
+ }
-template <typename T> struct ChatCommandHandlerToTuple { static_assert(!std::is_same_v<T,T>, "Invalid command handler signature"); };
-template <typename... Ts> struct ChatCommandHandlerToTuple<bool(*)(ChatHandler*, Ts...)> { using type = std::tuple<ChatHandler*, advstd::remove_cvref_t<Ts>...>; };
+ template <typename T> struct HandlerToTuple { static_assert(Trinity::dependant_false_v<T>, "Invalid command handler signature"); };
+ template <typename... Ts> struct HandlerToTuple<bool(*)(ChatHandler*, Ts...)> { using type = std::tuple<ChatHandler*, advstd::remove_cvref_t<Ts>...>; };
+ template <typename T> using TupleType = typename HandlerToTuple<T>::type;
+}
-template <typename T> struct ChatCommandStoreLastArg { static void store(T&, CommandArgs&) {} };
-template <> struct ChatCommandStoreLastArg<char const*> { static void store(char const*& arg, CommandArgs& args) { arg = args.GetRemainingArgs(); } };
-template <> struct ChatCommandStoreLastArg<CommandArgs*> { static void store(CommandArgs*& arg, CommandArgs& args) { arg = &args; } };
class TC_GAME_API ChatCommand
{
@@ -238,18 +145,12 @@ class TC_GAME_API ChatCommand
{
_wrapper = [](void* handler, ChatHandler* chatHandler, char const* argsStr)
{
- using tuple_type = typename ChatCommandHandlerToTuple<TypedHandler>::type;
+ using Tuple = Trinity::Impl::ChatCommands::TupleType<TypedHandler>;
- tuple_type arguments;
+ Tuple arguments;
std::get<0>(arguments) = chatHandler;
-
- CommandArgs args(argsStr);
- if (args.TryConsumeToTuple<1>(arguments))
- {
- auto& last = std::get<std::tuple_size_v<tuple_type>-1>(arguments);
- ChatCommandStoreLastArg<advstd::remove_cvref_t<decltype(last)>>::store(last, args);
+ if (Trinity::Impl::ChatCommands::ConsumeFromOffset<Tuple, 1>(arguments, argsStr))
return std::apply(reinterpret_cast<TypedHandler>(handler), std::move(arguments));
- }
else
return false;
};
@@ -272,7 +173,7 @@ class TC_GAME_API ChatCommand
bool HasHandler() const { return !!_handler; }
char const* Name;
- uint32 Permission; // function pointer required correct align (use uint32)
+ uint32 Permission;
bool AllowConsole;
std::string Help;
std::vector<ChatCommand> ChildCommands;
diff --git a/src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp b/src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp
index 9f62839d612..df74ac40e26 100644
--- a/src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp
+++ b/src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp
@@ -20,6 +20,7 @@
#include "DB2Stores.h"
#include "ObjectMgr.h"
#include "SpellMgr.h"
+#include "Util.h"
using namespace Trinity::ChatCommands;
@@ -29,12 +30,13 @@ struct AchievementVisitor
value_type operator()(Hyperlink<achievement> achData) const { return achData->Achievement; }
value_type operator()(uint32 achId) const { return sAchievementStore.LookupEntry(achId); }
};
-char const* Trinity::ChatCommands::ArgInfo<AchievementEntry const*>::TryConsume(AchievementEntry const*& data, char const* args)
+Optional<std::string_view> Trinity::Impl::ChatCommands::ArgInfo<AchievementEntry const*>::TryConsume(AchievementEntry const*& data, std::string_view args)
{
Variant<Hyperlink<achievement>, uint32> val;
- if ((args = CommandArgsConsumerSingle<decltype(val)>::TryConsumeTo(val, args)))
+ Optional<std::string_view> next = SingleConsumer<decltype(val)>::TryConsumeTo(val, args);
+ if (next)
data = val.visit(AchievementVisitor());
- return args;
+ return next;
}
struct CurrencyTypesVisitor
@@ -43,10 +45,11 @@ struct CurrencyTypesVisitor
value_type operator()(Hyperlink<currency> currency) const { return currency->Currency; }
value_type operator()(uint32 currencyId) const { return sCurrencyTypesStore.LookupEntry(currencyId); }
};
-char const* Trinity::ChatCommands::ArgInfo<CurrencyTypesEntry const*>::TryConsume(CurrencyTypesEntry const*& data, char const* args)
+Optional<std::string_view> Trinity::Impl::ChatCommands::ArgInfo<CurrencyTypesEntry const*>::TryConsume(CurrencyTypesEntry const*& data, std::string_view args)
{
Variant<Hyperlink<currency>, uint32> val;
- if ((args = CommandArgsConsumerSingle<decltype(val)>::TryConsumeTo(val, args)))
+ Optional<std::string_view> next = SingleConsumer<decltype(val)>::TryConsumeTo(val, args);
+ if (next)
data = val.visit(CurrencyTypesVisitor());
return args;
}
@@ -57,12 +60,13 @@ struct GameTeleVisitor
value_type operator()(Hyperlink<tele> tele) const { return sObjectMgr->GetGameTele(tele); }
value_type operator()(std::string const& tele) const { return sObjectMgr->GetGameTele(tele); }
};
-char const* Trinity::ChatCommands::ArgInfo<GameTele const*>::TryConsume(GameTele const*& data, char const* args)
+Optional<std::string_view> Trinity::Impl::ChatCommands::ArgInfo<GameTele const*>::TryConsume(GameTele const*& data, std::string_view args)
{
Variant<Hyperlink<tele>, std::string> val;
- if ((args = CommandArgsConsumerSingle<decltype(val)>::TryConsumeTo(val, args)))
+ Optional<std::string_view> next = SingleConsumer<decltype(val)>::TryConsumeTo(val, args);
+ if (next)
data = val.visit(GameTeleVisitor());
- return args;
+ return next;
}
struct SpellInfoVisitor
@@ -80,26 +84,11 @@ struct SpellInfoVisitor
value_type operator()(uint32 spellId) const { return sSpellMgr->GetSpellInfo(spellId, DIFFICULTY_NONE); }
};
-char const* Trinity::ChatCommands::ArgInfo<SpellInfo const*>::TryConsume(SpellInfo const*& data, char const* args)
+Optional<std::string_view> Trinity::Impl::ChatCommands::ArgInfo<SpellInfo const*>::TryConsume(SpellInfo const*& data, std::string_view args)
{
Variant<Hyperlink<apower>, Hyperlink<conduit>, Hyperlink<enchant>, Hyperlink<mawpower>, Hyperlink<pvptal>, Hyperlink<spell>, Hyperlink<talent>, Hyperlink<trade>, uint32> val;
- if ((args = CommandArgsConsumerSingle<decltype(val)>::TryConsumeTo(val, args)))
+ Optional<std::string_view> next = SingleConsumer<decltype(val)>::TryConsumeTo(val, args);
+ if (next)
data = val.visit(SpellInfoVisitor());
- return args;
-}
-
-char const* Trinity::ChatCommands::ArgInfo<bool>::TryConsume(bool& data, char const* args)
-{
- std::string val;
- if ((args = CommandArgsConsumerSingle<std::string>::TryConsumeTo(val, args)))
- {
- strToLower(val);
- if (val == "on" || val == "yes" || val == "true" || val == "1" || val == "y")
- data = true;
- else if (val == "off" || val == "no" || val == "false" || val == "0" || val == "n")
- data = false;
- else
- return nullptr;
- }
- return args;
+ return next;
}
diff --git a/src/server/game/Chat/ChatCommands/ChatCommandArgs.h b/src/server/game/Chat/ChatCommands/ChatCommandArgs.h
index e2270c0259f..22e96b1ccda 100644
--- a/src/server/game/Chat/ChatCommands/ChatCommandArgs.h
+++ b/src/server/game/Chat/ChatCommands/ChatCommandArgs.h
@@ -30,17 +30,15 @@
struct GameTele;
-namespace Trinity
-{
-namespace ChatCommands
+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&, char const* -> char const* *|
-|* returns nullptr if no match, otherwise pointer to first character of next token *|
-|* - if nullptr is returned, state of T& is indeterminate *|
+|* - 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 *|
|* *|
\****************************************************************************************/
@@ -51,16 +49,17 @@ struct ArgInfo { static_assert(!std::is_same_v<T,T>, "Invalid command parameter
template <typename T>
struct ArgInfo<T, std::enable_if_t<std::is_integral_v<T>>>
{
- static char const* TryConsume(T& val, char const* args)
+ static Optional<std::string_view> TryConsume(T& val, std::string_view args)
{
- char const* next = args;
- std::string_view token(args, Trinity::Impl::ChatCommands::tokenize(next));
+ auto [token, tail] = tokenize(args);
+ if (token.empty())
+ return std::nullopt;
if (Optional<T> v = StringTo<T>(token, 0))
val = *v;
else
- return nullptr;
- return next;
+ return std::nullopt;
+ return tail;
}
};
@@ -68,20 +67,26 @@ struct ArgInfo<T, std::enable_if_t<std::is_integral_v<T>>>
template <typename T>
struct ArgInfo<T, std::enable_if_t<std::is_floating_point_v<T>>>
{
- static char const* TryConsume(T& val, char const* args)
+ static Optional<std::string_view> TryConsume(T& val, std::string_view args)
{
- char const* next = args;
- std::string token(args, Trinity::Impl::ChatCommands::tokenize(next));
+ auto [token, tail] = tokenize(args);
+ if (token.empty())
+ return std::nullopt;
+
try
{
// @todo replace this once libc++ supports double args to from_chars for required minimum
size_t processedChars = 0;
- val = std::stold(token, &processedChars);
+ val = std::stold(std::string(token), &processedChars);
if (processedChars != token.length())
- return nullptr;
+ return std::nullopt;
}
- catch (...) { return nullptr; }
- return std::isfinite(val) ? next : nullptr;
+ catch (...) { return std::nullopt; }
+
+ if (std::isfinite(val))
+ return tail;
+ else
+ return std::nullopt;
}
};
@@ -89,16 +94,13 @@ struct ArgInfo<T, std::enable_if_t<std::is_floating_point_v<T>>>
template <>
struct ArgInfo<std::string_view, void>
{
- static char const* TryConsume(std::string_view& val, char const* args)
+ static Optional<std::string_view> TryConsume(std::string_view& val, std::string_view args)
{
- char const* next = args;
- if (size_t len = Trinity::Impl::ChatCommands::tokenize(next))
- {
- val = std::string_view(args, len);
- return next;
- }
- else
- return nullptr;
+ auto [token, next] = tokenize(args);
+ if (token.empty())
+ return std::nullopt;
+ val = token;
+ return next;
}
};
@@ -106,13 +108,13 @@ struct ArgInfo<std::string_view, void>
template <>
struct ArgInfo<std::string, void>
{
- static char const* TryConsume(std::string& val, char const* args)
+ static Optional<std::string_view> TryConsume(std::string& val, std::string_view args)
{
std::string_view view;
- args = ArgInfo<std::string_view>::TryConsume(view, args);
- if (args)
+ Optional<std::string_view> next = ArgInfo<std::string_view>::TryConsume(view, args);
+ if (next)
val.assign(view);
- return args;
+ return next;
}
};
@@ -120,18 +122,15 @@ struct ArgInfo<std::string, void>
template <>
struct ArgInfo<std::wstring, void>
{
- static char const* TryConsume(std::wstring& val, char const* args)
+ static Optional<std::string_view> TryConsume(std::wstring& val, std::string_view args)
{
std::string_view utf8view;
- char const* ret = ArgInfo<std::string_view>::TryConsume(utf8view, args);
-
- if (!ret)
- return nullptr;
-
- if (!Utf8toWStr(utf8view, val))
- return nullptr;
+ Optional<std::string_view> next = ArgInfo<std::string_view>::TryConsume(utf8view, args);
- return ret;
+ if (next && Utf8toWStr(utf8view, val))
+ return next;
+ else
+ return std::nullopt;
}
};
@@ -189,32 +188,31 @@ struct ArgInfo<T, std::enable_if_t<std::is_enum_v<T>>>
return nullptr;
}
- static char const* TryConsume(T& val, char const* args)
+ static Optional<std::string_view> TryConsume(T& val, std::string_view args)
{
std::string strVal;
- char const* ret = ArgInfo<std::string>::TryConsume(strVal, args);
-
- if (!ret)
- return nullptr;
+ Optional<std::string_view> next = ArgInfo<std::string>::TryConsume(strVal, args);
- if (T const* tmpVal = Match(strVal))
+ if (next)
{
- val = *tmpVal;
- return ret;
+ if (T const* match = Match(strVal))
+ {
+ val = *match;
+ return next;
+ }
}
// 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;
- ret = ArgInfo<U>::TryConsume(uVal, args);
- if (ret)
+ next = ArgInfo<U>::TryConsume(uVal, args);
+ if (next && EnumUtils::IsValid<T>(uVal))
{
val = static_cast<T>(uVal);
- if (!EnumUtils::IsValid(val))
- return nullptr;
+ return next;
}
- return ret;
+ return std::nullopt;
}
};
@@ -222,48 +220,72 @@ struct ArgInfo<T, std::enable_if_t<std::is_enum_v<T>>>
template <typename T>
struct ArgInfo<T, std::enable_if_t<std::is_base_of_v<ContainerTag, T>>>
{
- static char const* TryConsume(T& tag, char const* args)
+ static Optional<std::string_view> TryConsume(T& tag, std::string_view args)
{
return tag.TryConsume(args);
}
};
+template <typename T>
+struct ArgInfo<std::vector<T>, void>
+{
+ static Optional<std::string_view> TryConsume(std::vector<T>& val, std::string_view args)
+ {
+ val.clear();
+ Optional<std::string_view> next = ArgInfo<T>::TryConsume(val.emplace_back(), args);
+
+ if (!next)
+ return std::nullopt;
+
+ while (Optional<std::string_view> next2 = ArgInfo<T>::TryConsume(val.emplace_back(), *next))
+ next = next2;
+
+ val.pop_back();
+ return next;
+ }
+};
+
+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)
+ {
+ Optional<std::string_view> next = args;
+ for (T& t : val)
+ if (!(next = ArgInfo<T>::TryConsume(t, *next)))
+ return std::nullopt;
+ return next;
+ }
+};
+
// AchievementEntry* from numeric id or link
template <>
struct TC_GAME_API ArgInfo<AchievementEntry const*>
{
- static char const* TryConsume(AchievementEntry const*&, char const*);
+ static Optional<std::string_view> TryConsume(AchievementEntry const*&, std::string_view);
};
// CurrencyTypesEntry* from numeric id or link
template <>
struct TC_GAME_API ArgInfo<CurrencyTypesEntry const*>
{
- static char const* TryConsume(CurrencyTypesEntry const*&, char const*);
+ static Optional<std::string_view> TryConsume(CurrencyTypesEntry const*&, std::string_view);
};
// GameTele* from string name or link
template <>
struct TC_GAME_API ArgInfo<GameTele const*>
{
- static char const* TryConsume(GameTele const*&, char const*);
+ static Optional<std::string_view> TryConsume(GameTele const*&, std::string_view);
};
// SpellInfo const* from spell id or link
template <>
struct TC_GAME_API ArgInfo<SpellInfo const*>
{
- static char const* TryConsume(SpellInfo const*&, char const*);
+ static Optional<std::string_view> TryConsume(SpellInfo const*&, std::string_view);
};
-// bool from 1/0 or on/off
-template <>
-struct TC_GAME_API ArgInfo<bool>
-{
- static char const* TryConsume(bool&, char const*);
-};
-
-}
}
#endif
diff --git a/src/server/game/Chat/ChatCommands/ChatCommandHelpers.h b/src/server/game/Chat/ChatCommands/ChatCommandHelpers.h
index e22823e5d35..c6cf4f8ad94 100644
--- a/src/server/game/Chat/ChatCommands/ChatCommandHelpers.h
+++ b/src/server/game/Chat/ChatCommands/ChatCommandHelpers.h
@@ -18,10 +18,15 @@
#ifndef TRINITY_CHATCOMMANDHELPERS_H
#define TRINITY_CHATCOMMANDHELPERS_H
+#include <string_view>
#include <type_traits>
-namespace Trinity::ChatCommands
+namespace Trinity::Impl::ChatCommands
{
+ /***************** HELPERS *************************\
+ |* These really aren't for outside use... *|
+ \***************************************************/
+
static constexpr char COMMAND_DELIMITER = ' ';
template <typename T, typename = void>
@@ -32,19 +37,26 @@ namespace Trinity::ChatCommands
template <typename T>
using tag_base_t = typename tag_base<T>::type;
-}
-namespace Trinity::Impl::ChatCommands
-{
- /***************** HELPERS *************************\
- |* These really aren't for outside use... *|
- \***************************************************/
- inline std::size_t tokenize(char const*& end)
+ struct TokenizeResult {
+ explicit operator bool() { return !token.empty(); }
+ std::string_view token;
+ std::string_view tail;
+ };
+
+ inline TokenizeResult tokenize(std::string_view args)
{
- std::size_t len = 0;
- for (; *end && *end != Trinity::ChatCommands::COMMAND_DELIMITER; ++end, ++len);
- for (; *end && *end == Trinity::ChatCommands::COMMAND_DELIMITER; ++end);
- return len;
+ TokenizeResult result;
+ if (size_t delimPos = args.find(COMMAND_DELIMITER); delimPos != std::string_view::npos)
+ {
+ result.token = args.substr(0, delimPos);
+ if (size_t tailPos = args.find_first_not_of(COMMAND_DELIMITER, delimPos); tailPos != std::string_view::npos)
+ result.tail = args.substr(tailPos);
+ }
+ else
+ result.token = args;
+
+ return result;
}
template <typename T, typename... Ts>
diff --git a/src/server/game/Chat/ChatCommands/ChatCommandTags.h b/src/server/game/Chat/ChatCommands/ChatCommandTags.h
index 78f49673794..9255ef18fd3 100644
--- a/src/server/game/Chat/ChatCommands/ChatCommandTags.h
+++ b/src/server/game/Chat/ChatCommands/ChatCommandTags.h
@@ -22,59 +22,95 @@
#include "ChatCommandHelpers.h"
#include "Hyperlinks.h"
#include "Optional.h"
+#include "Util.h"
#include <cmath>
#include <cstring>
#include <iostream>
#include <string>
+#include <string_view>
#include <tuple>
#include <type_traits>
#include <utility>
#include <variant>
+namespace Trinity::Impl::ChatCommands
+{
+ struct ContainerTag {};
+ template <typename T>
+ struct tag_base<T, std::enable_if_t<std::is_base_of_v<ContainerTag, T>>>
+ {
+ using type = typename T::value_type;
+ };
+}
+
namespace Trinity::ChatCommands
{
/************************** CONTAINER TAGS **********************************************\
|* Simple holder classes to differentiate between extraction methods *|
- |* Should inherit from ContainerTag for template identification *|
+ |* Must inherit from Trinity::Impl::ChatCommands::ContainerTag *|
|* Must implement the following: *|
- |* - TryConsume: char const* -> char const* *|
- |* returns nullptr if no match, otherwise pointer to first character of next token *|
+ |* - TryConsume: std::string_view -> Optional<std::string_view> *|
+ |* returns nullopt if no match, otherwise the tail of the provided argument string *|
|* - typedef value_type of type that is contained within the tag *|
|* - cast operator to value_type *|
|* *|
\****************************************************************************************/
- struct ContainerTag {};
- template <typename T>
- struct tag_base<T, std::enable_if_t<std::is_base_of_v<ContainerTag, T>>>
- {
- using type = typename T::value_type;
- };
template <char c1, char... chars>
- struct ExactSequence : public ContainerTag
+ struct ExactSequence : Trinity::Impl::ChatCommands::ContainerTag
{
using value_type = void;
- static char const* _TryConsume(char const* pos)
+ static constexpr size_t N = (sizeof...(chars) + 1);
+
+ static bool Match(char const* pos)
+ {
+ if (*(pos++) != c1)
+ return false;
+ else if constexpr (sizeof...(chars) > 0)
+ return ExactSequence<chars...>::Match(pos);
+ else
+ return true;
+ }
+
+ Optional<std::string_view> TryConsume(std::string_view args) const
{
- if (*(pos++) == c1)
+ if ((N <= args.length()) && ExactSequence::Match(args.data()))
{
- if constexpr (sizeof...(chars) > 0)
- return ExactSequence<chars...>::_TryConsume(pos);
- else if (Trinity::Impl::ChatCommands::tokenize(pos)) /* we did not consume the entire token */
- return nullptr;
- else
- return pos;
+ auto [remainingToken, tail] = Trinity::Impl::ChatCommands::tokenize(args.substr(N));
+ if (remainingToken.empty()) // if this is not empty, then we did not consume the full token
+ return tail;
}
- else
- return nullptr;
+ return std::nullopt;
+ }
+ };
+
+ struct Tail : std::string_view, Trinity::Impl::ChatCommands::ContainerTag
+ {
+ using value_type = std::string_view;
+
+ Optional<std::string_view> TryConsume(std::string_view args)
+ {
+ std::string_view::operator=(args);
+ return std::string_view();
}
+ };
- char const* TryConsume(char const* pos) const { return ExactSequence::_TryConsume(pos); }
+ struct WTail : std::wstring, Trinity::Impl::ChatCommands::ContainerTag
+ {
+ using value_type = std::wstring;
+
+ Optional<std::string_view> TryConsume(std::string_view args)
+ {
+ if (Utf8toWStr(args, *this))
+ return std::string_view();
+ else
+ return std::nullopt;
+ }
};
template <typename linktag>
- struct Hyperlink : public ContainerTag
+ struct Hyperlink : Trinity::Impl::ChatCommands::ContainerTag
{
using value_type = typename linktag::value_type;
using storage_type = advstd::remove_cvref_t<value_type>;
@@ -84,29 +120,27 @@ namespace Trinity::ChatCommands
value_type operator*() const { return val; }
storage_type const* operator->() const { return &val; }
- char const* TryConsume(char const* pos)
+ Optional<std::string_view> TryConsume(std::string_view args)
{
- Trinity::Hyperlinks::HyperlinkInfo info = Trinity::Hyperlinks::ParseHyperlink(pos);
+ Trinity::Hyperlinks::HyperlinkInfo info = Trinity::Hyperlinks::ParseSingleHyperlink(args);
// invalid hyperlinks cannot be consumed
if (!info)
- return nullptr;
+ return std::nullopt;
// check if we got the right tag
- if (info.tag.second != strlen(linktag::tag()))
- return nullptr;
- if (strncmp(info.tag.first, linktag::tag(), strlen(linktag::tag())) != 0)
- return nullptr;
+ if (info.tag != linktag::tag())
+ return std::nullopt;
// store value
- if (!linktag::StoreTo(val, info.data.first, info.data.second))
- return nullptr;
+ if (!linktag::StoreTo(val, info.data))
+ return std::nullopt;
- // finally, skip to end of token
- pos = info.next;
- Trinity::Impl::ChatCommands::tokenize(pos);
-
- // return final pos
- return pos;
+ // finally, skip any potential delimiters
+ auto [token, next] = Trinity::Impl::ChatCommands::tokenize(info.tail);
+ if (token.empty()) /* empty token = first character is delimiter, skip past it */
+ return next;
+ else
+ return info.tail;
}
private:
@@ -138,8 +172,8 @@ namespace Trinity::ChatCommands
{
using base = std::variant<T1, Ts...>;
- using first_type = tag_base_t<T1>;
- static constexpr bool have_operators = Trinity::Impl::ChatCommands::are_all_assignable<first_type, tag_base_t<Ts>...>::value;
+ using first_type = Trinity::Impl::ChatCommands::tag_base_t<T1>;
+ static constexpr bool have_operators = Trinity::Impl::ChatCommands::are_all_assignable<first_type, Trinity::Impl::ChatCommands::tag_base_t<Ts>...>::value;
template <bool C = have_operators>
std::enable_if_t<C, first_type> operator*() const
diff --git a/src/server/game/Chat/HyperlinkTags.cpp b/src/server/game/Chat/HyperlinkTags.cpp
index 05a48ad65b4..f0d36065170 100644
--- a/src/server/game/Chat/HyperlinkTags.cpp
+++ b/src/server/game/Chat/HyperlinkTags.cpp
@@ -27,44 +27,50 @@ static constexpr char HYPERLINK_DATA_DELIMITER = ':';
class HyperlinkDataTokenizer
{
public:
- HyperlinkDataTokenizer(char const* pos, size_t len, bool allowEmptyTokens = false) : _pos(pos), _len(len), _allowEmptyTokens(allowEmptyTokens), _empty(false) {}
+ HyperlinkDataTokenizer(std::string_view str, bool allowEmptyTokens = false) : _str(str), _allowEmptyTokens(allowEmptyTokens) {}
- template <typename T>
- bool TryConsumeTo(T& val)
- {
- if (_empty)
- return false;
-
- char const* firstPos = _pos;
- size_t thisLen = 0;
- // find next delimiter
- for (; _len && *_pos != HYPERLINK_DATA_DELIMITER; --_len, ++_pos, ++thisLen);
- if (_len)
- --_len, ++_pos; // skip the delimiter
- else
- _empty = true;
-
- if (_allowEmptyTokens && !thisLen)
+ template <typename T>
+ bool TryConsumeTo(T& val)
{
- val = T();
+ if (IsEmpty())
+ {
+ if (_allowEmptyTokens)
+ {
+ val = T();
+ return true;
+ }
+ return false;
+ }
+
+ if (size_t off = _str.find(HYPERLINK_DATA_DELIMITER); off != std::string_view::npos)
+ {
+ if (!Trinity::Hyperlinks::LinkTags::base_tag::StoreTo(val, _str.substr(0, off)))
+ {
+ if (off != 0 || !_allowEmptyTokens)
+ return false;
+ val = T();
+ }
+ _str = _str.substr(off+1);
+ }
+ else
+ {
+ if (!Trinity::Hyperlinks::LinkTags::base_tag::StoreTo(val, _str))
+ return false;
+ _str = std::string_view();
+ }
return true;
}
- return Trinity::Hyperlinks::LinkTags::base_tag::StoreTo(val, firstPos, thisLen);
- }
-
- bool IsEmpty() { return _empty; }
+ bool IsEmpty() const { return _str.empty(); }
private:
- char const* _pos;
- size_t _len;
- bool _allowEmptyTokens;
- bool _empty;
+ std::string_view _str;
+ bool _allowEmptyTokens;
};
-bool Trinity::Hyperlinks::LinkTags::achievement::StoreTo(AchievementLinkData& val, char const* pos, size_t len)
+bool Trinity::Hyperlinks::LinkTags::achievement::StoreTo(AchievementLinkData& val, std::string_view text)
{
- HyperlinkDataTokenizer t(pos, len);
+ HyperlinkDataTokenizer t(text);
uint32 achievementId;
if (!t.TryConsumeTo(achievementId))
return false;
@@ -74,9 +80,9 @@ bool Trinity::Hyperlinks::LinkTags::achievement::StoreTo(AchievementLinkData& va
t.TryConsumeTo(val.Criteria[1]) && t.TryConsumeTo(val.Criteria[2]) && t.TryConsumeTo(val.Criteria[3]) && t.IsEmpty();
}
-bool Trinity::Hyperlinks::LinkTags::apower::StoreTo(ArtifactPowerLinkData& val, char const* pos, size_t len)
+bool Trinity::Hyperlinks::LinkTags::apower::StoreTo(ArtifactPowerLinkData& val, std::string_view text)
{
- HyperlinkDataTokenizer t(pos, len);
+ HyperlinkDataTokenizer t(text);
uint32 artifactPowerId;
if (!(t.TryConsumeTo(artifactPowerId) && t.TryConsumeTo(val.PurchasedRank) && t.TryConsumeTo(val.CurrentRankWithBonus) && t.IsEmpty()))
return false;
@@ -88,9 +94,9 @@ bool Trinity::Hyperlinks::LinkTags::apower::StoreTo(ArtifactPowerLinkData& val,
return true;
}
-bool Trinity::Hyperlinks::LinkTags::azessence::StoreTo(AzeriteEssenceLinkData& val, char const* pos, size_t len)
+bool Trinity::Hyperlinks::LinkTags::azessence::StoreTo(AzeriteEssenceLinkData& val, std::string_view text)
{
- HyperlinkDataTokenizer t(pos, len);
+ HyperlinkDataTokenizer t(text);
uint32 azeriteEssenceId;
if (!t.TryConsumeTo(azeriteEssenceId))
return false;
@@ -98,30 +104,31 @@ bool Trinity::Hyperlinks::LinkTags::azessence::StoreTo(AzeriteEssenceLinkData& v
&& sDB2Manager.GetAzeriteEssencePower(azeriteEssenceId, val.Rank) && t.IsEmpty();
}
-bool Trinity::Hyperlinks::LinkTags::battlepet::StoreTo(BattlePetLinkData& val, char const* pos, size_t len)
+bool Trinity::Hyperlinks::LinkTags::battlepet::StoreTo(BattlePetLinkData& val, std::string_view text)
{
- HyperlinkDataTokenizer t(pos, len);
+ HyperlinkDataTokenizer t(text);
uint32 battlePetSpeciesId;
if (!t.TryConsumeTo(battlePetSpeciesId))
return false;
return (val.Species = sBattlePetSpeciesStore.LookupEntry(battlePetSpeciesId)) && t.TryConsumeTo(val.Level)
&& t.TryConsumeTo(val.Quality) && val.Quality < MAX_ITEM_QUALITY
&& t.TryConsumeTo(val.MaxHealth) && t.TryConsumeTo(val.Power) && t.TryConsumeTo(val.Speed)
- && t.TryConsumeTo(val.PetGuid) && val.PetGuid.GetHigh() == HighGuid::BattlePet && t.TryConsumeTo(val.DisplayId);
+ && t.TryConsumeTo(val.PetGuid) && val.PetGuid.GetHigh() == HighGuid::BattlePet && t.TryConsumeTo(val.DisplayId)
+ && t.IsEmpty();
}
-bool Trinity::Hyperlinks::LinkTags::conduit::StoreTo(SoulbindConduitRankEntry const*& val, char const* pos, size_t len)
+bool Trinity::Hyperlinks::LinkTags::conduit::StoreTo(SoulbindConduitRankEntry const*& val, std::string_view text)
{
- HyperlinkDataTokenizer t(pos, len);
+ HyperlinkDataTokenizer t(text);
uint32 soulbindConduitId, rank;
if (!(t.TryConsumeTo(soulbindConduitId) && t.TryConsumeTo(rank) && t.IsEmpty()))
return false;
return !!(val = sDB2Manager.GetSoulbindConduitRank(soulbindConduitId, rank));
}
-bool Trinity::Hyperlinks::LinkTags::currency::StoreTo(CurrencyLinkData& val, char const* pos, size_t len)
+bool Trinity::Hyperlinks::LinkTags::currency::StoreTo(CurrencyLinkData& val, std::string_view text)
{
- HyperlinkDataTokenizer t(pos, len);
+ HyperlinkDataTokenizer t(text);
uint32 currencyId;
if (!t.TryConsumeTo(currencyId))
return false;
@@ -132,18 +139,18 @@ bool Trinity::Hyperlinks::LinkTags::currency::StoreTo(CurrencyLinkData& val, cha
return true;
}
-bool Trinity::Hyperlinks::LinkTags::enchant::StoreTo(SpellInfo const*& val, char const* pos, size_t len)
+bool Trinity::Hyperlinks::LinkTags::enchant::StoreTo(SpellInfo const*& val, std::string_view text)
{
- HyperlinkDataTokenizer t(pos, len);
+ HyperlinkDataTokenizer t(text);
uint32 spellId;
if (!(t.TryConsumeTo(spellId) && t.IsEmpty()))
return false;
return !!(val = sSpellMgr->GetSpellInfo(spellId, DIFFICULTY_NONE)) && val->HasAttribute(SPELL_ATTR0_TRADESPELL);
}
-bool Trinity::Hyperlinks::LinkTags::garrfollower::StoreTo(GarrisonFollowerLinkData& val, char const* pos, size_t len)
+bool Trinity::Hyperlinks::LinkTags::garrfollower::StoreTo(GarrisonFollowerLinkData& val, std::string_view text)
{
- HyperlinkDataTokenizer t(pos, len);
+ HyperlinkDataTokenizer t(text);
uint32 garrFollowerId;
if (!t.TryConsumeTo(garrFollowerId))
return false;
@@ -152,7 +159,7 @@ bool Trinity::Hyperlinks::LinkTags::garrfollower::StoreTo(GarrisonFollowerLinkDa
if (!val.Follower || !t.TryConsumeTo(val.Quality) || val.Quality >= MAX_ITEM_QUALITY || !t.TryConsumeTo(val.Level) || !t.TryConsumeTo(val.ItemLevel)
|| !t.TryConsumeTo(val.Abilities[0]) || !t.TryConsumeTo(val.Abilities[1]) || !t.TryConsumeTo(val.Abilities[2]) || !t.TryConsumeTo(val.Abilities[3])
|| !t.TryConsumeTo(val.Traits[0]) || !t.TryConsumeTo(val.Traits[1]) || !t.TryConsumeTo(val.Traits[2]) || !t.TryConsumeTo(val.Traits[3])
- || !t.TryConsumeTo(val.Specialization))
+ || !t.TryConsumeTo(val.Specialization) || !t.IsEmpty())
return false;
for (uint32 ability : val.Abilities)
@@ -169,27 +176,27 @@ bool Trinity::Hyperlinks::LinkTags::garrfollower::StoreTo(GarrisonFollowerLinkDa
return true;
}
-bool Trinity::Hyperlinks::LinkTags::garrfollowerability::StoreTo(GarrAbilityEntry const*& val, char const* pos, size_t len)
+bool Trinity::Hyperlinks::LinkTags::garrfollowerability::StoreTo(GarrAbilityEntry const*& val, std::string_view text)
{
- HyperlinkDataTokenizer t(pos, len);
+ HyperlinkDataTokenizer t(text);
uint32 garrAbilityId;
if (!t.TryConsumeTo(garrAbilityId))
return false;
return !!(val = sGarrAbilityStore.LookupEntry(garrAbilityId)) && t.IsEmpty();
}
-bool Trinity::Hyperlinks::LinkTags::garrmission::StoreTo(GarrisonMissionLinkData& val, char const* pos, size_t len)
+bool Trinity::Hyperlinks::LinkTags::garrmission::StoreTo(GarrisonMissionLinkData& val, std::string_view text)
{
- HyperlinkDataTokenizer t(pos, len);
+ HyperlinkDataTokenizer t(text);
uint32 garrMissionId;
if (!t.TryConsumeTo(garrMissionId))
return false;
return !!(val.Mission = sGarrMissionStore.LookupEntry(garrMissionId)) && t.TryConsumeTo(val.DbID) && t.IsEmpty();
}
-bool Trinity::Hyperlinks::LinkTags::instancelock::StoreTo(InstanceLockLinkData& val, char const* pos, size_t len)
+bool Trinity::Hyperlinks::LinkTags::instancelock::StoreTo(InstanceLockLinkData& val, std::string_view text)
{
- HyperlinkDataTokenizer t(pos, len);
+ HyperlinkDataTokenizer t(text);
if (!t.TryConsumeTo(val.Owner))
return false;
uint32 mapId;
@@ -200,9 +207,9 @@ bool Trinity::Hyperlinks::LinkTags::instancelock::StoreTo(InstanceLockLinkData&
&& t.TryConsumeTo(val.CompletedEncountersMask) && t.IsEmpty();
}
-bool Trinity::Hyperlinks::LinkTags::item::StoreTo(ItemLinkData& val, char const* pos, size_t len)
+bool Trinity::Hyperlinks::LinkTags::item::StoreTo(ItemLinkData& val, std::string_view text)
{
- HyperlinkDataTokenizer t(pos, len, true);
+ HyperlinkDataTokenizer t(text, true);
uint32 itemId, dummy, numBonusListIDs;
if (!t.TryConsumeTo(itemId))
return false;
@@ -260,9 +267,9 @@ bool Trinity::Hyperlinks::LinkTags::item::StoreTo(ItemLinkData& val, char const*
return t.TryConsumeTo(val.Creator) && t.TryConsumeTo(val.UseEnchantId) && t.IsEmpty();
}
-bool Trinity::Hyperlinks::LinkTags::journal::StoreTo(JournalLinkData& val, char const* pos, size_t len)
+bool Trinity::Hyperlinks::LinkTags::journal::StoreTo(JournalLinkData& val, std::string_view text)
{
- HyperlinkDataTokenizer t(pos, len);
+ HyperlinkDataTokenizer t(text);
uint32 id;
if (!t.TryConsumeTo(val.Type) || !t.TryConsumeTo(id) || !t.TryConsumeTo(val.Difficulty) || !t.IsEmpty())
return false;
@@ -306,9 +313,9 @@ bool Trinity::Hyperlinks::LinkTags::journal::StoreTo(JournalLinkData& val, char
return true;
}
-bool Trinity::Hyperlinks::LinkTags::keystone::StoreTo(KeystoneLinkData& val, char const* pos, size_t len)
+bool Trinity::Hyperlinks::LinkTags::keystone::StoreTo(KeystoneLinkData& val, std::string_view text)
{
- HyperlinkDataTokenizer t(pos, len);
+ HyperlinkDataTokenizer t(text);
uint32 mapChallengeModeId;
if (!t.TryConsumeTo(val.ItemId) || !t.TryConsumeTo(mapChallengeModeId) || !t.TryConsumeTo(val.Level)
|| !t.TryConsumeTo(val.Affix[0]) || !t.TryConsumeTo(val.Affix[1]) || !t.TryConsumeTo(val.Affix[2]) || !t.TryConsumeTo(val.Affix[3])
@@ -326,18 +333,18 @@ bool Trinity::Hyperlinks::LinkTags::keystone::StoreTo(KeystoneLinkData& val, cha
return true;
}
-bool Trinity::Hyperlinks::LinkTags::mawpower::StoreTo(MawPowerEntry const*& val, char const* pos, size_t len)
+bool Trinity::Hyperlinks::LinkTags::mawpower::StoreTo(MawPowerEntry const*& val, std::string_view text)
{
- HyperlinkDataTokenizer t(pos, len);
+ HyperlinkDataTokenizer t(text);
uint32 mawPowerId;
if (!t.TryConsumeTo(mawPowerId))
return false;
return !!(val = sMawPowerStore.LookupEntry(mawPowerId)) && t.IsEmpty();
}
-bool Trinity::Hyperlinks::LinkTags::pvptal::StoreTo(PvpTalentEntry const*& val, char const* pos, size_t len)
+bool Trinity::Hyperlinks::LinkTags::pvptal::StoreTo(PvpTalentEntry const*& val, std::string_view text)
{
- HyperlinkDataTokenizer t(pos, len);
+ HyperlinkDataTokenizer t(text);
uint32 pvpTalentId;
if (!(t.TryConsumeTo(pvpTalentId) && t.IsEmpty()))
return false;
@@ -346,18 +353,18 @@ bool Trinity::Hyperlinks::LinkTags::pvptal::StoreTo(PvpTalentEntry const*& val,
return true;
}
-bool Trinity::Hyperlinks::LinkTags::quest::StoreTo(QuestLinkData& val, char const* pos, size_t len)
+bool Trinity::Hyperlinks::LinkTags::quest::StoreTo(QuestLinkData& val, std::string_view text)
{
- HyperlinkDataTokenizer t(pos, len);
+ HyperlinkDataTokenizer t(text);
uint32 questId;
if (!t.TryConsumeTo(questId))
return false;
return (val.Quest = sObjectMgr->GetQuestTemplate(questId)) && t.TryConsumeTo(val.ContentTuningId) && t.IsEmpty();
}
-bool Trinity::Hyperlinks::LinkTags::spell::StoreTo(SpellLinkData& val, char const* pos, size_t len)
+bool Trinity::Hyperlinks::LinkTags::spell::StoreTo(SpellLinkData& val, std::string_view text)
{
- HyperlinkDataTokenizer t(pos, len);
+ HyperlinkDataTokenizer t(text);
uint32 spellId, glyphPropertiesId;
if (!(t.TryConsumeTo(spellId) && t.TryConsumeTo(glyphPropertiesId) && t.IsEmpty()))
return false;
@@ -365,9 +372,9 @@ bool Trinity::Hyperlinks::LinkTags::spell::StoreTo(SpellLinkData& val, char cons
&& (!glyphPropertiesId || !!(val.Glyph = sGlyphPropertiesStore.LookupEntry(glyphPropertiesId)));
}
-bool Trinity::Hyperlinks::LinkTags::talent::StoreTo(TalentEntry const*& val, char const* pos, size_t len)
+bool Trinity::Hyperlinks::LinkTags::talent::StoreTo(TalentEntry const*& val, std::string_view text)
{
- HyperlinkDataTokenizer t(pos, len);
+ HyperlinkDataTokenizer t(text);
uint32 talentId;
if (!(t.TryConsumeTo(talentId) && t.IsEmpty()))
return false;
@@ -376,9 +383,9 @@ bool Trinity::Hyperlinks::LinkTags::talent::StoreTo(TalentEntry const*& val, cha
return true;
}
-bool Trinity::Hyperlinks::LinkTags::trade::StoreTo(TradeskillLinkData& val, char const* pos, size_t len)
+bool Trinity::Hyperlinks::LinkTags::trade::StoreTo(TradeskillLinkData& val, std::string_view text)
{
- HyperlinkDataTokenizer t(pos, len);
+ HyperlinkDataTokenizer t(text);
uint32 spellId, skillId;
if (!t.TryConsumeTo(val.Owner) || !t.TryConsumeTo(spellId) || !t.TryConsumeTo(skillId) || !t.IsEmpty())
return false;
@@ -389,18 +396,18 @@ bool Trinity::Hyperlinks::LinkTags::trade::StoreTo(TradeskillLinkData& val, char
return true;
}
-bool Trinity::Hyperlinks::LinkTags::transmogappearance::StoreTo(ItemModifiedAppearanceEntry const*& val, char const* pos, size_t len)
+bool Trinity::Hyperlinks::LinkTags::transmogappearance::StoreTo(ItemModifiedAppearanceEntry const*& val, std::string_view text)
{
- HyperlinkDataTokenizer t(pos, len);
+ HyperlinkDataTokenizer t(text);
uint32 itemModifiedAppearanceId;
if (!t.TryConsumeTo(itemModifiedAppearanceId))
return false;
return !!(val = sItemModifiedAppearanceStore.LookupEntry(itemModifiedAppearanceId)) && t.IsEmpty();
}
-bool Trinity::Hyperlinks::LinkTags::transmogillusion::StoreTo(SpellItemEnchantmentEntry const*& val, char const* pos, size_t len)
+bool Trinity::Hyperlinks::LinkTags::transmogillusion::StoreTo(SpellItemEnchantmentEntry const*& val, std::string_view text)
{
- HyperlinkDataTokenizer t(pos, len);
+ HyperlinkDataTokenizer t(text);
uint32 spellItemEnchantmentId;
if (!t.TryConsumeTo(spellItemEnchantmentId))
return false;
@@ -408,18 +415,18 @@ bool Trinity::Hyperlinks::LinkTags::transmogillusion::StoreTo(SpellItemEnchantme
&& sDB2Manager.GetTransmogIllusionForEnchantment(spellItemEnchantmentId) && t.IsEmpty();
}
-bool Trinity::Hyperlinks::LinkTags::transmogset::StoreTo(TransmogSetEntry const*& val, char const* pos, size_t len)
+bool Trinity::Hyperlinks::LinkTags::transmogset::StoreTo(TransmogSetEntry const*& val, std::string_view text)
{
- HyperlinkDataTokenizer t(pos, len);
+ HyperlinkDataTokenizer t(text);
uint32 transmogSetId;
if (!t.TryConsumeTo(transmogSetId))
return false;
return !!(val = sTransmogSetStore.LookupEntry(transmogSetId)) && t.IsEmpty();
}
-bool Trinity::Hyperlinks::LinkTags::worldmap::StoreTo(WorldMapLinkData& val, char const* pos, size_t len)
+bool Trinity::Hyperlinks::LinkTags::worldmap::StoreTo(WorldMapLinkData& val, std::string_view text)
{
- HyperlinkDataTokenizer t(pos, len);
+ HyperlinkDataTokenizer t(text);
uint32 uiMapId;
if (!t.TryConsumeTo(uiMapId))
return false;
diff --git a/src/server/game/Chat/Hyperlinks.cpp b/src/server/game/Chat/Hyperlinks.cpp
index 88a4dcfae2b..387e723ce11 100644
--- a/src/server/game/Chat/Hyperlinks.cpp
+++ b/src/server/game/Chat/Hyperlinks.cpp
@@ -33,87 +33,95 @@ using namespace Trinity::Hyperlinks;
inline uint8 toHex(char c) { return (c >= '0' && c <= '9') ? c - '0' + 0x10 : (c >= 'a' && c <= 'f') ? c - 'a' + 0x1a : 0x00; }
// Validates a single hyperlink
-HyperlinkInfo Trinity::Hyperlinks::ParseHyperlink(char const* pos)
+HyperlinkInfo Trinity::Hyperlinks::ParseSingleHyperlink(std::string_view str)
{
- //color tag
- if (*(pos++) != '|' || *(pos++) != 'c')
- return nullptr;
uint32 color = 0;
+ std::string_view tag;
+ std::string_view data;
+ std::string_view text;
+
+ //color tag
+ if (str.substr(0, 2) != "|c")
+ return {};
+ str.remove_prefix(2);
+
+ if (str.length() < 8)
+ return {};
+
for (uint8 i = 0; i < 8; ++i)
{
- if (uint8 hex = toHex(*(pos++)))
+ if (uint8 hex = toHex(str[i]))
color = (color << 4) | (hex & 0xf);
else
- return nullptr;
- }
- // link data start tag
- if (*(pos++) != '|' || *(pos++) != 'H')
- return nullptr;
- // link tag, find next : or |
- char const* tagStart = pos;
- size_t tagLength = 0;
- while (*pos && *pos != '|' && *(pos++) != ':') // we only advance pointer to one past if the last thing is : (not for |), this is intentional!
- ++tagLength;
- // ok, link data, skip to next |
- char const* dataStart = pos;
- size_t dataLength = 0;
- while (*pos && *(pos++) != '|')
- ++dataLength;
+ return {};
+ }
+ str.remove_prefix(8);
+
+ if (str.substr(0, 2) != "|H")
+ return {};
+ str.remove_prefix(2);
+
+ // tag+data part follows
+ if (size_t delimPos = str.find('|'); delimPos != std::string_view::npos)
+ {
+ tag = str.substr(0, delimPos);
+ str.remove_prefix(delimPos+1);
+ }
+ else
+ return {};
+
+ // split tag if : is present (data separator)
+ if (size_t dataStart = tag.find(':'); dataStart != std::string_view::npos)
+ {
+ data = tag.substr(dataStart+1);
+ tag = tag.substr(0, dataStart);
+ }
+
// ok, next should be link data end tag...
- if (*(pos++) != 'h')
- return nullptr;
- // then visible link text, starts with [
- if (*(pos++) != '[')
- return nullptr;
- // skip until we hit the next ], abort on unexpected |
- char const* textStart = pos;
- size_t textLength = 0;
- while (*pos)
- {
- if (*pos == '|')
- return nullptr;
- if (*(pos++) == ']')
- break;
- ++textLength;
- }
- // link end tag
- if (*(pos++) != '|' || *(pos++) != 'h' || *(pos++) != '|' || *(pos++) != 'r')
- return nullptr;
+ if (str.substr(0, 1) != "h")
+ return {};
+ str.remove_prefix(1);
+ // skip to final |
+ if (size_t end = str.find('|'); end != std::string_view::npos)
+ {
+ // check end tag
+ if (str.substr(end, 4) != "|h|r")
+ return {};
+ // check text brackets
+ if ((str[0] != '[') || (str[end - 1] != ']'))
+ return {};
+ text = str.substr(1, end - 2);
+ // tail
+ str = str.substr(end + 4);
+ }
+ else
+ return {};
+
// ok, valid hyperlink, return info
- return { pos, color, tagStart, tagLength, dataStart, dataLength, textStart, textLength };
+ return { str, color, tag, data, text };
}
template <typename T>
struct LinkValidator
{
- static bool IsTextValid(typename T::value_type, char const*, size_t) { return true; }
+ static bool IsTextValid(typename T::value_type, std::string_view) { return true; }
static bool IsColorValid(typename T::value_type, HyperlinkColor) { return true; }
};
-// str1 is null-terminated, str2 is length-terminated, check if they are exactly equal
-static bool equal_with_len(char const* str1, char const* str2, size_t len)
-{
- if (!*str1)
- return false;
- while (len && *str1 && *(str1++) == *(str2++))
- --len;
- return !len && !*str1;
-}
-
-static bool IsCreatureNameValid(uint32 creatureId, char const* pos, size_t len)
+static bool IsCreatureNameValid(uint32 creatureId, std::string_view text)
{
if (CreatureTemplate const* creatureTemplate = sObjectMgr->GetCreatureTemplate(creatureId))
{
CreatureLocale const* locale = sObjectMgr->GetCreatureLocale(creatureId);
if (!locale)
- return equal_with_len(creatureTemplate->Name.c_str(), pos, len);
+ return creatureTemplate->Name == text;
for (uint8 i = 0; i < TOTAL_LOCALES; ++i)
{
std::string const& name = (i == DEFAULT_LOCALE) ? creatureTemplate->Name : locale->Name[i];
if (name.empty())
continue;
- if (equal_with_len(name.c_str(), pos, len))
+ if (name == text)
return true;
}
}
@@ -124,15 +132,15 @@ static bool IsCreatureNameValid(uint32 creatureId, char const* pos, size_t len)
template <>
struct LinkValidator<LinkTags::spell>
{
- static bool IsTextValid(SpellLinkData const& data, char const* pos, size_t len)
+ static bool IsTextValid(SpellLinkData const& data, std::string_view text)
{
- return IsTextValid(data.Spell, pos, len);
+ return IsTextValid(data.Spell, text);
}
- static bool IsTextValid(SpellInfo const* info, char const* pos, size_t len)
+ static bool IsTextValid(SpellInfo const* info, std::string_view text)
{
for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
- if (equal_with_len((*info->SpellName)[i], pos, len))
+ if ((*info->SpellName)[i] == text)
return true;
return false;
}
@@ -146,12 +154,12 @@ struct LinkValidator<LinkTags::spell>
template <>
struct LinkValidator<LinkTags::achievement>
{
- static bool IsTextValid(AchievementLinkData const& data, char const* pos, size_t len)
+ static bool IsTextValid(AchievementLinkData const& data, std::string_view text)
{
- if (!len)
+ if (text.empty())
return false;
for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
- if (equal_with_len(data.Achievement->Title[i], pos, len))
+ if (text == data.Achievement->Title[i])
return true;
return false;
}
@@ -165,10 +173,10 @@ struct LinkValidator<LinkTags::achievement>
template <>
struct LinkValidator<LinkTags::apower>
{
- static bool IsTextValid(ArtifactPowerLinkData const& data, char const* pos, size_t len)
+ static bool IsTextValid(ArtifactPowerLinkData const& data, std::string_view text)
{
if (SpellInfo const* info = sSpellMgr->GetSpellInfo(data.ArtifactPower->SpellID, DIFFICULTY_NONE))
- return LinkValidator<LinkTags::spell>::IsTextValid(info, pos, len);
+ return LinkValidator<LinkTags::spell>::IsTextValid(info, text);
return false;
}
@@ -181,10 +189,10 @@ struct LinkValidator<LinkTags::apower>
template <>
struct LinkValidator<LinkTags::azessence>
{
- static bool IsTextValid(AzeriteEssenceLinkData const& data, char const* pos, size_t len)
+ static bool IsTextValid(AzeriteEssenceLinkData const& data, std::string_view text)
{
for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
- if (equal_with_len(data.Essence->Name[i], pos, len))
+ if (data.Essence->Name[i] == text)
return true;
return false;
}
@@ -198,9 +206,9 @@ struct LinkValidator<LinkTags::azessence>
template <>
struct LinkValidator<LinkTags::battlepet>
{
- static bool IsTextValid(BattlePetLinkData const& data, char const* pos, size_t len)
+ static bool IsTextValid(BattlePetLinkData const& data, std::string_view text)
{
- return IsCreatureNameValid(data.Species->CreatureID, pos, len);
+ return IsCreatureNameValid(data.Species->CreatureID, text);
}
static bool IsColorValid(BattlePetLinkData const& data, HyperlinkColor c)
@@ -212,10 +220,10 @@ struct LinkValidator<LinkTags::battlepet>
template <>
struct LinkValidator<LinkTags::conduit>
{
- static bool IsTextValid(SoulbindConduitRankEntry const* rank, char const* pos, size_t len)
+ static bool IsTextValid(SoulbindConduitRankEntry const* rank, std::string_view text)
{
if (SpellInfo const* info = sSpellMgr->GetSpellInfo(rank->SpellID, DIFFICULTY_NONE))
- return LinkValidator<LinkTags::spell>::IsTextValid(info, pos, len);
+ return LinkValidator<LinkTags::spell>::IsTextValid(info, text);
return false;
}
@@ -228,11 +236,11 @@ struct LinkValidator<LinkTags::conduit>
template <>
struct LinkValidator<LinkTags::currency>
{
- static bool IsTextValid(CurrencyLinkData const& data, char const* pos, size_t len)
+ static bool IsTextValid(CurrencyLinkData const& data, std::string_view text)
{
LocalizedString const* name = data.Container ? &data.Container->ContainerName : &data.Currency->Name;
for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
- if (equal_with_len((*name)[i], pos, len))
+ if ((*name)[i] == text)
return true;
return false;
}
@@ -246,9 +254,9 @@ struct LinkValidator<LinkTags::currency>
template <>
struct LinkValidator<LinkTags::enchant>
{
- static bool IsTextValid(SpellInfo const* info, char const* pos, size_t len)
+ static bool IsTextValid(SpellInfo const* info, std::string_view text)
{
- if (LinkValidator<LinkTags::spell>::IsTextValid(info, pos, len))
+ if (LinkValidator<LinkTags::spell>::IsTextValid(info, text))
return true;
SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(info->Id);
if (bounds.first == bounds.second)
@@ -264,11 +272,12 @@ struct LinkValidator<LinkTags::enchant>
for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
{
- char const* skillName = skill->DisplayName[i];
- size_t skillLen = strlen(skillName);
- if (len > skillLen + 2 && // or of form [Skill Name: Spell Name]
- !strncmp(pos, skillName, skillLen) && !strncmp(pos + skillLen, ": ", 2) &&
- equal_with_len((*info->SpellName)[i], pos + (skillLen + 2), len - (skillLen + 2)))
+ std::string_view skillName = skill->DisplayName[i];
+ std::string_view spellName = (*info->SpellName)[i];
+ if ((text.length() == (skillName.length() + 2 + spellName.length())) &&
+ (text.substr(0, skillName.length()) == skillName) &&
+ (text.substr(skillName.length(), 2) == ": ") &&
+ (text.substr(skillName.length() + 2) == spellName))
return true;
}
}
@@ -284,10 +293,10 @@ struct LinkValidator<LinkTags::enchant>
template <>
struct LinkValidator<LinkTags::garrfollower>
{
- static bool IsTextValid(GarrisonFollowerLinkData const& data, char const* pos, size_t len)
+ static bool IsTextValid(GarrisonFollowerLinkData const& data, std::string_view text)
{
- return IsCreatureNameValid(data.Follower->HordeCreatureID, pos, len)
- || IsCreatureNameValid(data.Follower->AllianceCreatureID, pos, len);
+ return IsCreatureNameValid(data.Follower->HordeCreatureID, text)
+ || IsCreatureNameValid(data.Follower->AllianceCreatureID, text);
}
static bool IsColorValid(GarrisonFollowerLinkData const& data, HyperlinkColor c)
@@ -299,10 +308,10 @@ struct LinkValidator<LinkTags::garrfollower>
template <>
struct LinkValidator<LinkTags::garrfollowerability>
{
- static bool IsTextValid(GarrAbilityEntry const* ability, char const* pos, size_t len)
+ static bool IsTextValid(GarrAbilityEntry const* ability, std::string_view text)
{
for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
- if (equal_with_len(ability->Name[i], pos, len))
+ if (ability->Name[i] == text)
return true;
return false;
}
@@ -316,10 +325,10 @@ struct LinkValidator<LinkTags::garrfollowerability>
template <>
struct LinkValidator<LinkTags::garrmission>
{
- static bool IsTextValid(GarrisonMissionLinkData const& data, char const* pos, size_t len)
+ static bool IsTextValid(GarrisonMissionLinkData const& data, std::string_view text)
{
for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
- if (equal_with_len(data.Mission->Name[i], pos, len))
+ if (data.Mission->Name[i] == text)
return true;
return false;
}
@@ -333,10 +342,10 @@ struct LinkValidator<LinkTags::garrmission>
template <>
struct LinkValidator<LinkTags::instancelock>
{
- static bool IsTextValid(InstanceLockLinkData const& data, char const* pos, size_t len)
+ static bool IsTextValid(InstanceLockLinkData const& data, std::string_view text)
{
for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
- if (equal_with_len(data.Map->MapName[i], pos, len))
+ if (data.Map->MapName[i] == text)
return true;
return false;
}
@@ -350,16 +359,16 @@ struct LinkValidator<LinkTags::instancelock>
template <>
struct LinkValidator<LinkTags::item>
{
- static bool IsTextValid(ItemLinkData const& data, char const* pos, size_t len)
+ static bool IsTextValid(ItemLinkData const& data, std::string_view text)
{
LocalizedString const* suffixStrings = nullptr;
if (!data.Item->HasFlag(ITEM_FLAG3_HIDE_NAME_SUFFIX) && data.Suffix)
suffixStrings = &data.Suffix->Description;
- return IsTextValid(data.Item, suffixStrings, pos, len);
+ return IsTextValid(data.Item, suffixStrings, text);
}
- static bool IsTextValid(ItemTemplate const* itemTemplate, LocalizedString const* suffixStrings, char const* pos, size_t len)
+ static bool IsTextValid(ItemTemplate const* itemTemplate, LocalizedString const* suffixStrings, std::string_view text)
{
for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
{
@@ -368,13 +377,17 @@ struct LinkValidator<LinkTags::item>
continue;
if (suffixStrings)
{
- if (len > name.length() + 1 &&
- (strncmp(name.c_str(), pos, name.length()) == 0) &&
- (*(pos + name.length()) == ' ') &&
- equal_with_len((*suffixStrings)[i], pos + name.length() + 1, len - name.length() - 1))
+ std::string_view suffix = (*suffixStrings)[i];
+ if (
+ (!suffix.empty()) &&
+ (text.length() == (name.length() + 1 + suffix.length())) &&
+ (text.substr(0, name.length()) == name) &&
+ (text[name.length()] == ' ') &&
+ (text.substr(name.length() + 1) == suffix)
+ )
return true;
}
- else if (equal_with_len(name.c_str(), pos, len))
+ else if (text == name)
return true;
}
return false;
@@ -389,10 +402,10 @@ struct LinkValidator<LinkTags::item>
template <>
struct LinkValidator<LinkTags::journal>
{
- static bool IsTextValid(JournalLinkData const& data, char const* pos, size_t len)
+ static bool IsTextValid(JournalLinkData const& data, std::string_view text)
{
for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
- if (equal_with_len((*data.ExpectedText)[i], pos, len))
+ if ((*data.ExpectedText)[i] == text)
return true;
return false;
}
@@ -406,21 +419,20 @@ struct LinkValidator<LinkTags::journal>
template <>
struct LinkValidator<LinkTags::keystone>
{
- static bool IsTextValid(KeystoneLinkData const& data, char const* pos, size_t len)
+ static bool IsTextValid(KeystoneLinkData const& data, std::string_view text)
{
// Skip "Keystone" prefix - not loading GlobalStrings.db2
- char const* validateStartPos = strstr(pos, ": ");
- if (!validateStartPos)
+ size_t validateStartPos = text.find(": ");
+ if (validateStartPos == std::string_view::npos)
return false;
- // skip ": " too
- validateStartPos += 2;
- size_t validateLen = len - (validateStartPos - pos);
+ text.remove_prefix(validateStartPos);
+ text.remove_prefix(2); // skip ": " too
for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
{
std::string expectedText = Trinity::StringFormat("%s (%u)", data.Map->Name[i], data.Level);
- if (equal_with_len(expectedText.c_str(), validateStartPos, validateLen))
+ if (expectedText == text)
return true;
}
return false;
@@ -435,18 +447,18 @@ struct LinkValidator<LinkTags::keystone>
template <>
struct LinkValidator<LinkTags::quest>
{
- static bool IsTextValid(QuestLinkData const& data, char const* pos, size_t len)
+ static bool IsTextValid(QuestLinkData const& data, std::string_view text)
{
QuestTemplateLocale const* locale = sObjectMgr->GetQuestLocale(data.Quest->GetQuestId());
if (!locale)
- return equal_with_len(data.Quest->GetLogTitle().c_str(), pos, len);
+ return data.Quest->GetLogTitle().c_str() == text;
for (uint8 i = 0; i < TOTAL_LOCALES; ++i)
{
std::string const& name = (i == DEFAULT_LOCALE) ? data.Quest->GetLogTitle() : locale->LogTitle[i];
if (name.empty())
continue;
- if (equal_with_len(name.c_str(), pos, len))
+ if (text == name)
return true;
}
@@ -465,10 +477,10 @@ struct LinkValidator<LinkTags::quest>
template <>
struct LinkValidator<LinkTags::mawpower>
{
- static bool IsTextValid(MawPowerEntry const* mawPower, char const* pos, size_t len)
+ static bool IsTextValid(MawPowerEntry const* mawPower, std::string_view text)
{
if (SpellInfo const* info = sSpellMgr->GetSpellInfo(mawPower->SpellID, DIFFICULTY_NONE))
- return LinkValidator<LinkTags::spell>::IsTextValid(info, pos, len);
+ return LinkValidator<LinkTags::spell>::IsTextValid(info, text);
return false;
}
@@ -481,7 +493,7 @@ struct LinkValidator<LinkTags::mawpower>
template <>
struct LinkValidator<LinkTags::outfit>
{
- static bool IsTextValid(std::string const&, char const*, size_t)
+ static bool IsTextValid(std::string const&, std::string_view)
{
return true;
}
@@ -495,10 +507,10 @@ struct LinkValidator<LinkTags::outfit>
template <>
struct LinkValidator<LinkTags::pvptal>
{
- static bool IsTextValid(PvpTalentEntry const* pvpTalent, char const* pos, size_t len)
+ static bool IsTextValid(PvpTalentEntry const* pvpTalent, std::string_view text)
{
if (SpellInfo const* info = sSpellMgr->GetSpellInfo(pvpTalent->SpellID, DIFFICULTY_NONE))
- return LinkValidator<LinkTags::spell>::IsTextValid(info, pos, len);
+ return LinkValidator<LinkTags::spell>::IsTextValid(info, text);
return false;
}
@@ -511,10 +523,10 @@ struct LinkValidator<LinkTags::pvptal>
template <>
struct LinkValidator<LinkTags::talent>
{
- static bool IsTextValid(TalentEntry const* talent, char const* pos, size_t len)
+ static bool IsTextValid(TalentEntry const* talent, std::string_view text)
{
if (SpellInfo const* info = sSpellMgr->GetSpellInfo(talent->SpellID, DIFFICULTY_NONE))
- return LinkValidator<LinkTags::spell>::IsTextValid(info, pos, len);
+ return LinkValidator<LinkTags::spell>::IsTextValid(info, text);
return false;
}
@@ -527,9 +539,9 @@ struct LinkValidator<LinkTags::talent>
template <>
struct LinkValidator<LinkTags::trade>
{
- static bool IsTextValid(TradeskillLinkData const& data, char const* pos, size_t len)
+ static bool IsTextValid(TradeskillLinkData const& data, std::string_view text)
{
- return LinkValidator<LinkTags::spell>::IsTextValid(data.Spell, pos, len);
+ return LinkValidator<LinkTags::spell>::IsTextValid(data.Spell, text);
}
static bool IsColorValid(TradeskillLinkData const&, HyperlinkColor c)
@@ -541,10 +553,10 @@ struct LinkValidator<LinkTags::trade>
template <>
struct LinkValidator<LinkTags::transmogappearance>
{
- static bool IsTextValid(ItemModifiedAppearanceEntry const* enchantment, char const* pos, size_t len)
+ static bool IsTextValid(ItemModifiedAppearanceEntry const* enchantment, std::string_view text)
{
if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(enchantment->ItemID))
- return LinkValidator<LinkTags::item>::IsTextValid(itemTemplate, nullptr, pos, len);
+ return LinkValidator<LinkTags::item>::IsTextValid(itemTemplate, nullptr, text);
return false;
}
@@ -557,13 +569,13 @@ struct LinkValidator<LinkTags::transmogappearance>
template <>
struct LinkValidator<LinkTags::transmogillusion>
{
- static bool IsTextValid(SpellItemEnchantmentEntry const* enchantment, char const* pos, size_t len)
+ static bool IsTextValid(SpellItemEnchantmentEntry const* enchantment, std::string_view text)
{
for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
- if (equal_with_len(enchantment->Name[i], pos, len))
+ if (enchantment->Name[i] == text)
return true;
for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
- if (equal_with_len(enchantment->HordeName[i], pos, len))
+ if (enchantment->HordeName[i] == text)
return true;
return false;
}
@@ -577,17 +589,17 @@ struct LinkValidator<LinkTags::transmogillusion>
template <>
struct LinkValidator<LinkTags::transmogset>
{
- static bool IsTextValid(TransmogSetEntry const* set, char const* pos, size_t len)
+ static bool IsTextValid(TransmogSetEntry const* set, std::string_view text)
{
for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
{
if (ItemNameDescriptionEntry const* itemNameDescription = sItemNameDescriptionStore.LookupEntry(set->ItemNameDescriptionID))
{
std::string expectedText = Trinity::StringFormat("%s (%s)", set->Name[i], itemNameDescription->Description[i]);
- if (equal_with_len(expectedText.c_str(), pos, len))
+ if (expectedText.c_str() == text)
return true;
}
- else if (equal_with_len(set->Name[i], pos, len))
+ else if (set->Name[i] == text)
return true;
}
return false;
@@ -602,7 +614,7 @@ struct LinkValidator<LinkTags::transmogset>
template <>
struct LinkValidator<LinkTags::worldmap>
{
- static bool IsTextValid(WorldMapLinkData const&, char const*, size_t)
+ static bool IsTextValid(WorldMapLinkData const&, std::string_view)
{
return true;
}
@@ -615,17 +627,16 @@ struct LinkValidator<LinkTags::worldmap>
#define TryValidateAs(tagname) \
{ \
- ASSERT(!strcmp(LinkTags::tagname::tag(), #tagname)); \
- if (info.tag.second == strlen(LinkTags::tagname::tag()) && \
- !strncmp(info.tag.first, LinkTags::tagname::tag(), strlen(LinkTags::tagname::tag()))) \
+ static_assert(LinkTags::tagname::tag() == #tagname); \
+ if (info.tag == LinkTags::tagname::tag()) \
{ \
advstd::remove_cvref_t<typename LinkTags::tagname::value_type> t; \
- if (!LinkTags::tagname::StoreTo(t, info.data.first, info.data.second)) \
+ if (!LinkTags::tagname::StoreTo(t, info.data)) \
return false; \
if (!LinkValidator<LinkTags::tagname>::IsColorValid(t, info.color)) \
return false; \
if (sWorld->getIntConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY)) \
- if (!LinkValidator<LinkTags::tagname>::IsTextValid(t, info.text.first, info.text.second)) \
+ if (!LinkValidator<LinkTags::tagname>::IsTextValid(t, info.text)) \
return false; \
return true; \
} \
@@ -675,16 +686,19 @@ static bool ValidateLinkInfo(HyperlinkInfo const& info)
}
// Validates all hyperlinks and control sequences contained in str
-bool Trinity::Hyperlinks::CheckAllLinks(std::string const& str)
+bool Trinity::Hyperlinks::CheckAllLinks(std::string_view str)
{
// Step 1: Disallow all control sequences except ||, |H, |h, |c and |r
{
- std::string::size_type pos = 0;
+ std::string_view::size_type pos = 0;
while ((pos = str.find('|', pos)) != std::string::npos)
{
- char next = str[pos + 1];
+ ++pos;
+ if (pos == str.length())
+ return false;
+ char next = str[pos];
if (next == 'H' || next == 'h' || next == 'c' || next == 'r' || next == '|')
- pos += 2;
+ ++pos;
else
return false;
}
@@ -697,21 +711,21 @@ bool Trinity::Hyperlinks::CheckAllLinks(std::string const& str)
// - <linkdata> is arbitrary length, no | contained
// - <linktext> is printable
{
- std::string::size_type pos = 0;
- while ((pos = str.find('|', pos)) != std::string::npos)
+ std::string::size_type pos;
+ while ((pos = str.find('|')) != std::string::npos)
{
if (str[pos + 1] == '|') // this is an escaped pipe character (||)
{
- pos += 2;
+ str = str.substr(pos + 2);
continue;
}
- HyperlinkInfo info = ParseHyperlink(str.c_str() + pos);
+ HyperlinkInfo info = ParseSingleHyperlink(str.substr(pos));
if (!info || !ValidateLinkInfo(info))
return false;
// tag is fine, find the next one
- pos = info.next - str.c_str();
+ str = info.tail;
}
}
diff --git a/src/server/game/Chat/Hyperlinks.h b/src/server/game/Chat/Hyperlinks.h
index 2085a27c4e5..dfc3f656ccf 100644
--- a/src/server/game/Chat/Hyperlinks.h
+++ b/src/server/game/Chat/Hyperlinks.h
@@ -21,6 +21,7 @@
#include "ObjectGuid.h"
#include "StringConvert.h"
#include <string>
+#include <string_view>
#include <type_traits>
#include <utility>
@@ -204,26 +205,32 @@ namespace Trinity::Hyperlinks
|* Link tags must abide by the following: *|
|* - MUST expose ::value_type typedef *|
|* - storage type is remove_cvref_t<value_type> *|
- |* - MUST expose static ::tag method, void -> const char* *|
+ |* - MUST expose static ::tag method, void -> std::string_view *|
|* - this method SHOULD be constexpr *|
|* - returns identifier string for the link ("creature", "creature_entry", "item") *|
- |* - MUST expose static ::StoreTo method, (storage&, char const*, size_t) *|
- |* - assign value_type& based on content of std::string(char const*, size_t) *|
+ |* - MUST expose static ::StoreTo method, (storage&, std::string_view) *|
+ |* - assign value_type& based on content of std::string_view *|
|* - return value indicates success/failure *|
|* - for integral/string types this can be achieved by extending base_tag *|
\****************************************************************************************/
struct base_tag
{
- static bool StoreTo(std::string& val, char const* pos, size_t len)
+ static bool StoreTo(std::string_view& val, std::string_view data)
{
- val.assign(pos, len);
+ val = data;
+ return true;
+ }
+
+ static bool StoreTo(std::string& val, std::string_view data)
+ {
+ val.assign(data);
return true;
}
template <typename T>
- static std::enable_if_t<std::is_integral_v<T>, bool> StoreTo(T& val, char const* pos, size_t len)
+ static std::enable_if_t<std::is_integral_v<T>, bool> StoreTo(T& val, std::string_view data)
{
- if (Optional<T> res = Trinity::StringTo<T>(std::string_view(pos, len)))
+ if (Optional<T> res = Trinity::StringTo<T>(data))
{
val = *res;
return true;
@@ -232,9 +239,9 @@ namespace Trinity::Hyperlinks
return false;
}
- static bool StoreTo(ObjectGuid& val, char const* pos, size_t len)
+ static bool StoreTo(ObjectGuid& val, std::string_view data)
{
- ObjectGuid parsed = ObjectGuid::FromString(std::string(pos, len));
+ ObjectGuid parsed = ObjectGuid::FromString(std::string(data));
if (parsed != ObjectGuid::FromStringFailed)
{
val = parsed;
@@ -245,7 +252,7 @@ namespace Trinity::Hyperlinks
}
};
- #define make_base_tag(ltag, type) struct ltag : public base_tag { using value_type = type; static constexpr char const* tag() { return #ltag; } }
+ #define make_base_tag(ltag, type) struct ltag : public base_tag { using value_type = type; static constexpr std::string_view tag() { return #ltag; } }
// custom formats
make_base_tag(area, uint32);
make_base_tag(areatrigger, uint32);
@@ -268,176 +275,176 @@ namespace Trinity::Hyperlinks
struct TC_GAME_API achievement
{
using value_type = AchievementLinkData const&;
- static constexpr char const* tag() { return "achievement"; }
- static bool StoreTo(AchievementLinkData& val, char const* pos, size_t len);
+ static constexpr std::string_view tag() { return "achievement"; }
+ static bool StoreTo(AchievementLinkData& val, std::string_view text);
};
struct TC_GAME_API apower
{
using value_type = ArtifactPowerLinkData const&;
- static constexpr char const* tag() { return "apower"; }
- static bool StoreTo(ArtifactPowerLinkData& val, char const* pos, size_t len);
+ static constexpr std::string_view tag() { return "apower"; }
+ static bool StoreTo(ArtifactPowerLinkData& val, std::string_view text);
};
struct TC_GAME_API azessence
{
using value_type = AzeriteEssenceLinkData const&;
- static constexpr char const* tag() { return "azessence"; }
- static bool StoreTo(AzeriteEssenceLinkData& val, char const* pos, size_t len);
+ static constexpr std::string_view tag() { return "azessence"; }
+ static bool StoreTo(AzeriteEssenceLinkData& val, std::string_view text);
};
struct TC_GAME_API battlepet
{
using value_type = BattlePetLinkData const&;
- static constexpr char const* tag() { return "battlepet"; }
- static bool StoreTo(BattlePetLinkData& val, char const* pos, size_t len);
+ static constexpr std::string_view tag() { return "battlepet"; }
+ static bool StoreTo(BattlePetLinkData& val, std::string_view text);
};
struct TC_GAME_API conduit
{
using value_type = SoulbindConduitRankEntry const*;
- static constexpr char const* tag() { return "conduit"; }
- static bool StoreTo(SoulbindConduitRankEntry const*& val, char const* pos, size_t len);
+ static constexpr std::string_view tag() { return "conduit"; }
+ static bool StoreTo(SoulbindConduitRankEntry const*& val, std::string_view text);
};
struct TC_GAME_API currency
{
using value_type = CurrencyLinkData const&;
- static constexpr char const* tag() { return "currency"; }
- static bool StoreTo(CurrencyLinkData& val, char const* pos, size_t len);
+ static constexpr std::string_view tag() { return "currency"; }
+ static bool StoreTo(CurrencyLinkData& val, std::string_view text);
};
struct TC_GAME_API enchant
{
using value_type = SpellInfo const*;
- static constexpr char const* tag() { return "enchant"; }
- static bool StoreTo(SpellInfo const*& val, char const* pos, size_t len);
+ static constexpr std::string_view tag() { return "enchant"; }
+ static bool StoreTo(SpellInfo const*& val, std::string_view text);
};
struct TC_GAME_API garrfollower
{
using value_type = GarrisonFollowerLinkData const&;
- static constexpr char const* tag() { return "garrfollower"; }
- static bool StoreTo(GarrisonFollowerLinkData& val, char const* pos, size_t len);
+ static constexpr std::string_view tag() { return "garrfollower"; }
+ static bool StoreTo(GarrisonFollowerLinkData& val, std::string_view text);
};
struct TC_GAME_API garrfollowerability
{
using value_type = GarrAbilityEntry const*;
- static constexpr char const* tag() { return "garrfollowerability"; }
- static bool StoreTo(GarrAbilityEntry const*& val, char const* pos, size_t len);
+ static constexpr std::string_view tag() { return "garrfollowerability"; }
+ static bool StoreTo(GarrAbilityEntry const*& val, std::string_view text);
};
struct TC_GAME_API garrmission
{
using value_type = GarrisonMissionLinkData const&;
- static constexpr char const* tag() { return "garrmission"; }
- static bool StoreTo(GarrisonMissionLinkData& val, char const* pos, size_t len);
+ static constexpr std::string_view tag() { return "garrmission"; }
+ static bool StoreTo(GarrisonMissionLinkData& val, std::string_view text);
};
struct TC_GAME_API instancelock
{
using value_type = InstanceLockLinkData const&;
- static constexpr char const* tag() { return "instancelock"; }
- static bool StoreTo(InstanceLockLinkData& val, char const* pos, size_t len);
+ static constexpr std::string_view tag() { return "instancelock"; }
+ static bool StoreTo(InstanceLockLinkData& val, std::string_view text);
};
struct TC_GAME_API item
{
using value_type = ItemLinkData const&;
- static constexpr char const* tag() { return "item"; }
- static bool StoreTo(ItemLinkData& val, char const* pos, size_t len);
+ static constexpr std::string_view tag() { return "item"; }
+ static bool StoreTo(ItemLinkData& val, std::string_view text);
};
struct TC_GAME_API journal
{
using value_type = JournalLinkData const&;
- static constexpr char const* tag() { return "journal"; }
- static bool StoreTo(JournalLinkData& val, char const* pos, size_t len);
+ static constexpr std::string_view tag() { return "journal"; }
+ static bool StoreTo(JournalLinkData& val, std::string_view text);
};
struct TC_GAME_API keystone
{
using value_type = KeystoneLinkData const&;
- static constexpr char const* tag() { return "keystone"; }
- static bool StoreTo(KeystoneLinkData& val, char const* pos, size_t len);
+ static constexpr std::string_view tag() { return "keystone"; }
+ static bool StoreTo(KeystoneLinkData& val, std::string_view text);
};
struct TC_GAME_API mawpower
{
using value_type = MawPowerEntry const*;
- static constexpr char const* tag() { return "mawpower"; }
- static bool StoreTo(MawPowerEntry const*& val, char const* pos, size_t len);
+ static constexpr std::string_view tag() { return "mawpower"; }
+ static bool StoreTo(MawPowerEntry const*& val, std::string_view text);
};
struct TC_GAME_API pvptal
{
using value_type = PvpTalentEntry const*;
- static constexpr char const* tag() { return "pvptal"; }
- static bool StoreTo(PvpTalentEntry const*& val, char const* pos, size_t len);
+ static constexpr std::string_view tag() { return "pvptal"; }
+ static bool StoreTo(PvpTalentEntry const*& val, std::string_view text);
};
struct TC_GAME_API quest
{
using value_type = QuestLinkData const&;
- static constexpr char const* tag() { return "quest"; }
- static bool StoreTo(QuestLinkData& val, char const* pos, size_t len);
+ static constexpr std::string_view tag() { return "quest"; }
+ static bool StoreTo(QuestLinkData& val, std::string_view text);
};
struct TC_GAME_API spell
{
using value_type = SpellLinkData const&;
- static constexpr char const* tag() { return "spell"; }
- static bool StoreTo(SpellLinkData& val, char const* pos, size_t len);
+ static constexpr std::string_view tag() { return "spell"; }
+ static bool StoreTo(SpellLinkData& val, std::string_view text);
};
struct TC_GAME_API talent
{
using value_type = TalentEntry const*;
- static constexpr char const* tag() { return "talent"; }
- static bool StoreTo(TalentEntry const*& val, char const* pos, size_t len);
+ static constexpr std::string_view tag() { return "talent"; }
+ static bool StoreTo(TalentEntry const*& val, std::string_view text);
};
struct TC_GAME_API trade
{
using value_type = TradeskillLinkData const&;
- static constexpr char const* tag() { return "trade"; }
- static bool StoreTo(TradeskillLinkData& val, char const* pos, size_t len);
+ static constexpr std::string_view tag() { return "trade"; }
+ static bool StoreTo(TradeskillLinkData& val, std::string_view text);
};
struct TC_GAME_API transmogappearance
{
using value_type = ItemModifiedAppearanceEntry const*;
- static constexpr char const* tag() { return "transmogappearance"; }
- static bool StoreTo(ItemModifiedAppearanceEntry const*& val, char const* pos, size_t len);
+ static constexpr std::string_view tag() { return "transmogappearance"; }
+ static bool StoreTo(ItemModifiedAppearanceEntry const*& val, std::string_view text);
};
struct TC_GAME_API transmogillusion
{
using value_type = SpellItemEnchantmentEntry const*;
- static constexpr char const* tag() { return "transmogillusion"; }
- static bool StoreTo(SpellItemEnchantmentEntry const*& val, char const* pos, size_t len);
+ static constexpr std::string_view tag() { return "transmogillusion"; }
+ static bool StoreTo(SpellItemEnchantmentEntry const*& val, std::string_view text);
};
struct TC_GAME_API transmogset
{
using value_type = TransmogSetEntry const*;
- static constexpr char const* tag() { return "transmogset"; }
- static bool StoreTo(TransmogSetEntry const*& val, char const* pos, size_t len);
+ static constexpr std::string_view tag() { return "transmogset"; }
+ static bool StoreTo(TransmogSetEntry const*& val, std::string_view text);
};
struct TC_GAME_API worldmap
{
using value_type = WorldMapLinkData const&;
- static constexpr char const* tag() { return "worldmap"; }
- static bool StoreTo(WorldMapLinkData& val, char const* pos, size_t len);
+ static constexpr std::string_view tag() { return "worldmap"; }
+ static bool StoreTo(WorldMapLinkData& val, std::string_view text);
};
}
struct HyperlinkColor
{
HyperlinkColor(uint32 c) : r(c >> 16), g(c >> 8), b(c), a(c >> 24) {}
- uint8 r, g, b, a;
+ uint8 const r, g, b, a;
bool operator==(uint32 c) const
{
if ((c & 0xff) ^ b)
@@ -454,18 +461,20 @@ namespace Trinity::Hyperlinks
struct HyperlinkInfo
{
- HyperlinkInfo(char const* n = nullptr, uint32 c = 0, char const* tS = nullptr, size_t tL = 0, char const* dS = nullptr, size_t dL = 0, char const* cS = nullptr, size_t cL = 0) :
- next(n), color(c), tag(tS, tL), data(dS, dL), text(cS, cL) {}
+ HyperlinkInfo() : ok(false), color(0) {}
+ HyperlinkInfo(std::string_view t, uint32 c, std::string_view ta, std::string_view d, std::string_view te) :
+ ok(true), tail(t), color(c), tag(ta), data(d), text(te) {}
- explicit operator bool() { return next; }
- char const* const next;
+ explicit operator bool() { return ok; }
+ bool const ok;
+ std::string_view const tail;
HyperlinkColor const color;
- std::pair<char const*, size_t> const tag;
- std::pair<char const*, size_t> const data;
- std::pair<char const*, size_t> const text;
+ std::string_view const tag;
+ std::string_view const data;
+ std::string_view const text;
};
- HyperlinkInfo TC_GAME_API ParseHyperlink(char const* pos);
- bool TC_GAME_API CheckAllLinks(std::string const&);
+ HyperlinkInfo TC_GAME_API ParseSingleHyperlink(std::string_view str);
+ bool TC_GAME_API CheckAllLinks(std::string_view str);
}
diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp
index 1e9c07ca554..ce9d0952f73 100644
--- a/src/server/scripts/Commands/cs_debug.cpp
+++ b/src/server/scripts/Commands/cs_debug.cpp
@@ -1681,7 +1681,7 @@ public:
return true;
};
- static bool HandleDebugOutOfBounds([[maybe_unused]] ChatHandler* handler, CommandArgs* /*args*/)
+ static bool HandleDebugOutOfBounds([[maybe_unused]] ChatHandler* handler)
{
#ifdef ASAN
uint8 stack_array[10] = {};
@@ -1838,7 +1838,7 @@ public:
return true;
}
- static bool HandleDebugDummyCommand(ChatHandler* handler, CommandArgs* /*args*/)
+ static bool HandleDebugDummyCommand(ChatHandler* handler)
{
handler->SendSysMessage("This command does nothing right now. Edit your local core (cs_debug.cpp) to make it do whatever you need for testing.");
return true;
diff --git a/src/server/scripts/Commands/cs_message.cpp b/src/server/scripts/Commands/cs_message.cpp
index 8fbf85fda83..157961594ab 100644
--- a/src/server/scripts/Commands/cs_message.cpp
+++ b/src/server/scripts/Commands/cs_message.cpp
@@ -130,62 +130,60 @@ public:
return true;
}
- static bool HandleNameAnnounceCommand(ChatHandler* handler, CommandArgs* args)
+ static bool HandleNameAnnounceCommand(ChatHandler* handler, Tail message)
{
- if (!*args)
+ if (message.empty())
return false;
std::string name("Console");
if (WorldSession* session = handler->GetSession())
name = session->GetPlayer()->GetName();
- sWorld->SendWorldText(LANG_ANNOUNCE_COLOR, name.c_str(), args->GetFullArgs());
+ sWorld->SendWorldText(LANG_ANNOUNCE_COLOR, name.c_str(), message.data());
return true;
}
- static bool HandleGMNameAnnounceCommand(ChatHandler* handler, CommandArgs* args)
+ static bool HandleGMNameAnnounceCommand(ChatHandler* handler, Tail message)
{
- if (!*args)
+ if (message.empty())
return false;
std::string name("Console");
if (WorldSession* session = handler->GetSession())
name = session->GetPlayer()->GetName();
- sWorld->SendGMText(LANG_GM_ANNOUNCE_COLOR, name.c_str(), args->GetFullArgs());
+ sWorld->SendGMText(LANG_GM_ANNOUNCE_COLOR, name.c_str(), message.data());
return true;
}
// global announce
- static bool HandleAnnounceCommand(ChatHandler* handler, char const* args)
+ static bool HandleAnnounceCommand(ChatHandler* handler, Tail message)
{
- if (!*args)
+ if (message.empty())
return false;
- std::string str = handler->PGetParseString(LANG_SYSTEMMESSAGE, args);
-
- sWorld->SendServerMessage(SERVER_MSG_STRING, str);
+ sWorld->SendServerMessage(SERVER_MSG_STRING, Trinity::StringFormat(handler->GetTrinityString(LANG_SYSTEMMESSAGE), message.data()).c_str());
return true;
}
// announce to logged in GMs
- static bool HandleGMAnnounceCommand(ChatHandler* /*handler*/, CommandArgs* args)
+ static bool HandleGMAnnounceCommand(ChatHandler* /*handler*/, Tail message)
{
- if (!*args)
+ if (message.empty())
return false;
- sWorld->SendGMText(LANG_GM_BROADCAST, args->GetFullArgs());
+ sWorld->SendGMText(LANG_GM_BROADCAST, message.data());
return true;
}
// send on-screen notification to players
- static bool HandleNotifyCommand(ChatHandler* handler, CommandArgs* args)
+ static bool HandleNotifyCommand(ChatHandler* handler, Tail message)
{
- if (!*args)
+ if (message.empty())
return false;
std::string str = handler->GetTrinityString(LANG_GLOBAL_NOTIFY);
- str += args->GetFullArgs();
+ str += message;
sWorld->SendGlobalMessage(WorldPackets::Chat::PrintNotification(str).Write());
@@ -193,13 +191,13 @@ public:
}
// send on-screen notification to GMs
- static bool HandleGMNotifyCommand(ChatHandler* handler, CommandArgs* args)
+ static bool HandleGMNotifyCommand(ChatHandler* handler, Tail message)
{
- if (!*args)
+ if (message.empty())
return false;
std::string str = handler->GetTrinityString(LANG_GM_NOTIFY);
- str += args->GetFullArgs();
+ str += message;
sWorld->SendGlobalGMMessage(WorldPackets::Chat::PrintNotification(str).Write());
diff --git a/tests/game/ChatCommand.cpp b/tests/game/ChatCommand.cpp
index 5aab641deba..f484b5806c5 100644
--- a/tests/game/ChatCommand.cpp
+++ b/tests/game/ChatCommand.cpp
@@ -27,7 +27,7 @@ static void TestChatCommand(char const* c, F f, Optional<bool> expected = true)
{
bool r = ChatCommand("", 0, false, +f, "")(nullptr, c);
if (expected)
- ASSERT(r == *expected);
+ REQUIRE(r == *expected);
}
TEST_CASE("Command return pass-through", "[ChatCommand]")
@@ -38,27 +38,51 @@ TEST_CASE("Command return pass-through", "[ChatCommand]")
TEST_CASE("Command argument parsing", "[ChatCommand]")
{
- TestChatCommand("42", [](ChatHandler*, uint32 u)
+ SECTION("Single uint32 argument")
{
- REQUIRE(u == 42);
- return true;
- });
+ TestChatCommand("42", [](ChatHandler*, uint32 u)
+ {
+ REQUIRE(u == 42);
+ return true;
+ });
+ TestChatCommand("true", [](ChatHandler*, uint32) { return true; }, false);
+ }
- TestChatCommand("true", [](ChatHandler*, uint32) { return true; }, false);
+ SECTION("std::vector<uint8>")
+ {
+ TestChatCommand("1 2 3 4 5 6 7 8 9 10", [](ChatHandler*, std::vector<uint8> v)
+ {
+ REQUIRE(v.size() == 10);
+ for (size_t i = 0; i < 10; ++i)
+ REQUIRE(v[i] == (i + 1));
+ return true;
+ });
+ }
- TestChatCommand("1 2 3 4 5 6 7 8 9 10", [](ChatHandler*, std::vector<uint8> v)
+ SECTION("Hyperlink<player>")
{
- REQUIRE(v.size() == 10);
- for (size_t i = 0; i < 10; ++i)
- REQUIRE(v[i] == (i + 1));
- return true;
- });
+ TestChatCommand("|cffff0000|Hplayer:Test|h[Test]|h|r",
+ [](ChatHandler*, Hyperlink<player> player)
+ {
+ REQUIRE("Test"sv == *player);
+ return true;
+ }
+ );
+ }
- TestChatCommand("|cffff0000|Hplayer:Test|h[Test]|h|r",
- [](ChatHandler*, Hyperlink<player> player)
+ SECTION("Two strings")
+ {
+ TestChatCommand("two strings", [](ChatHandler*, std::string_view v1, std::string_view v2)
+ {
+ REQUIRE(v1 == "two");
+ REQUIRE(v2 == "strings");
+ return true;
+ });
+ TestChatCommand("two strings", [](ChatHandler*, std::string_view) { return true; }, false);
+ TestChatCommand("two strings", [](ChatHandler*, Tail t)
{
- REQUIRE("Test"sv == *player);
+ REQUIRE(t == "two strings");
return true;
- }
- );
+ });
+ }
}