aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Chat/ChatCommands
diff options
context:
space:
mode:
authorTreeston <treeston.mmoc@gmail.com>2020-08-30 02:50:25 +0200
committerShauren <shauren.trinity@gmail.com>2022-02-04 21:21:17 +0100
commit45e9e943115badd1a10ce18dc660408564e4aac9 (patch)
tree3e573ba9d5da2c8eada7c71f923b5fcdc814459c /src/server/game/Chat/ChatCommands
parent175fb7056b32d5455b59d83cc0f46fc55d905307 (diff)
Core/ChatCommands: C++17 cleanup (again) (PR #25323)
(cherry picked from commit 2f7d2ef3e979ecd0536f3a3713e56c8e59652a47)
Diffstat (limited to 'src/server/game/Chat/ChatCommands')
-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
5 files changed, 281 insertions, 323 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