TrinityCore
ChatCommand.h
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#ifndef TRINITY_CHATCOMMAND_H
19#define TRINITY_CHATCOMMAND_H
20
21#include "ChatCommandArgs.h"
22#include "ChatCommandTags.h"
23#include "Define.h"
24#include "Errors.h"
25#include "Language.h"
26#include "Optional.h"
27#include "RBAC.h"
28#include "StringFormat.h"
29#include "Util.h"
30#include <cstddef>
31#include <map>
32#include <utility>
33#include <tuple>
34#include <type_traits>
35#include <variant>
36#include <vector>
37
38class ChatHandler;
39
41{
42 enum class Console : bool
43 {
44 No = false,
45 Yes = true
46 };
47
48 struct ChatCommandBuilder;
49 using ChatCommandTable = std::vector<ChatCommandBuilder>;
50}
51
53{
54 // forward declaration
55 // ConsumeFromOffset contains the bounds check for offset, then hands off to MultiConsumer
56 // the call stack is MultiConsumer -> ConsumeFromOffset -> MultiConsumer -> ConsumeFromOffset etc
57 // MultiConsumer goes into ArgInfo for parsing on each iteration
58 template <typename Tuple, size_t offset>
59 ChatCommandResult ConsumeFromOffset(Tuple&, ChatHandler const* handler, std::string_view args);
60
61 template <typename Tuple, typename NextType, size_t offset>
63 {
64 static ChatCommandResult TryConsumeTo(Tuple& tuple, ChatHandler const* handler, std::string_view args)
65 {
66 ChatCommandResult next = ArgInfo<NextType>::TryConsume(std::get<offset>(tuple), handler, args);
67 if (next)
68 return ConsumeFromOffset<Tuple, offset + 1>(tuple, handler, *next);
69 else
70 return next;
71 }
72 };
73
74 template <typename Tuple, typename NestedNextType, size_t offset>
75 struct MultiConsumer<Tuple, Optional<NestedNextType>, offset>
76 {
77 static ChatCommandResult TryConsumeTo(Tuple& tuple, ChatHandler const* handler, std::string_view args)
78 {
79 // try with the argument
80 auto& myArg = std::get<offset>(tuple);
81 myArg.emplace();
82
83 ChatCommandResult result1 = ArgInfo<NestedNextType>::TryConsume(myArg.value(), handler, args);
84 if (result1)
85 if ((result1 = ConsumeFromOffset<Tuple, offset + 1>(tuple, handler, *result1)))
86 return result1;
87 // try again omitting the argument
88 myArg = std::nullopt;
89 ChatCommandResult result2 = ConsumeFromOffset<Tuple, offset + 1>(tuple, handler, args);
90 if (result2)
91 return result2;
92 if (result1.HasErrorMessage() && result2.HasErrorMessage())
93 {
94 return Trinity::StringFormat("{} \"{}\"\n{} \"{}\"",
97 }
98 else if (result1.HasErrorMessage())
99 return result1;
100 else
101 return result2;
102 }
103 };
104
105 template <typename Tuple, size_t offset>
106 ChatCommandResult ConsumeFromOffset([[maybe_unused]] Tuple& tuple, [[maybe_unused]] ChatHandler const* handler, std::string_view args)
107 {
108 if constexpr (offset < std::tuple_size_v<Tuple>)
109 return MultiConsumer<Tuple, std::tuple_element_t<offset, Tuple>, offset>::TryConsumeTo(tuple, handler, args);
110 else if (!args.empty()) /* the entire string must be consumed */
111 return std::nullopt;
112 else
113 return args;
114 }
115
116 template <typename T> struct HandlerToTuple { static_assert(Trinity::dependant_false_v<T>, "Invalid command handler signature"); };
117 template <typename... Ts> struct HandlerToTuple<bool(ChatHandler*, Ts...)> { using type = std::tuple<ChatHandler*, std::remove_cvref_t<Ts>...>; };
118 template <typename T> using TupleType = typename HandlerToTuple<T>::type;
119
121 {
122 CommandInvoker() : _wrapper(nullptr), _handler(nullptr) {}
123 template <typename TypedHandler>
124 CommandInvoker(TypedHandler& handler)
125 {
126 _wrapper = [](void* handler, ChatHandler* chatHandler, std::string_view argsStr)
127 {
128 using Tuple = TupleType<TypedHandler>;
129
130 Tuple arguments;
131 std::get<0>(arguments) = chatHandler;
132 ChatCommandResult result = ConsumeFromOffset<Tuple, 1>(arguments, chatHandler, argsStr);
133 if (result)
134 return std::apply(reinterpret_cast<TypedHandler*>(handler), std::move(arguments));
135 else
136 {
137 if (result.HasErrorMessage())
138 SendErrorMessageToHandler(chatHandler, result.GetErrorMessage());
139 return false;
140 }
141 };
142 _handler = reinterpret_cast<void*>(handler);
143 }
144 CommandInvoker(bool(&handler)(ChatHandler*, char const*))
145 {
146 _wrapper = [](void* handler, ChatHandler* chatHandler, std::string_view argsStr)
147 {
148 // make a copy of the argument string
149 // legacy handlers can destroy input strings with strtok
150 std::string argsStrCopy(argsStr);
151 return reinterpret_cast<bool(*)(ChatHandler*, char const*)>(handler)(chatHandler, argsStrCopy.c_str());
152 };
153 _handler = reinterpret_cast<void*>(handler);
154 }
155
156 explicit operator bool() const { return (_wrapper != nullptr); }
157 bool operator()(ChatHandler* chatHandler, std::string_view args) const
158 {
160 return _wrapper(_handler, chatHandler, args);
161 }
162
163 private:
164 using wrapper_func = bool(void*, ChatHandler*, std::string_view);
166 void* _handler;
167 };
168
170 {
175 };
176
178 {
181
182 public:
183 static void LoadCommandMap();
184 static void InvalidateCommandMap();
185 static bool TryExecuteCommand(ChatHandler& handler, std::string_view cmd);
186 static void SendCommandHelpFor(ChatHandler& handler, std::string_view cmd);
187 static std::vector<std::string> GetAutoCompletionsFor(ChatHandler const& handler, std::string_view cmd);
188
190
191 private:
192 static std::map<std::string_view, ChatCommandNode, StringCompareLessI_T> const& GetTopLevelMap();
193 static void LoadCommandsIntoMap(ChatCommandNode* blank, std::map<std::string_view, ChatCommandNode, StringCompareLessI_T>& map, Trinity::ChatCommands::ChatCommandTable const& commands);
194
195 void LoadFromBuilder(ChatCommandBuilder const& builder);
197
198 void ResolveNames(std::string name);
199 void SendCommandHelp(ChatHandler& handler) const;
200
201 bool IsVisible(ChatHandler const& who) const { return (IsInvokerVisible(who) || HasVisibleSubCommands(who)); }
202 bool IsInvokerVisible(ChatHandler const& who) const;
203 bool HasVisibleSubCommands(ChatHandler const& who) const;
204
205 std::string _name;
208 std::variant<std::monostate, TrinityStrings, std::string> _help;
209 std::map<std::string_view, ChatCommandNode, StringCompareLessI_T> _subCommands;
210 };
211}
212
213namespace Trinity::ChatCommands
214{
216 {
219 {
220 template <typename T>
222 : _invoker{ handler }, _help{ help }, _permissions{ permission, allowConsole }
223 {}
224 InvokerEntry(InvokerEntry const&) = default;
226
230 };
231 using SubCommandEntry = std::reference_wrapper<std::vector<ChatCommandBuilder> const>;
232
235
236 template <typename TypedHandler>
237 ChatCommandBuilder(char const* name, TypedHandler& handler, TrinityStrings help, rbac::RBACPermissions permission, Trinity::ChatCommands::Console allowConsole)
238 : _name{ ASSERT_NOTNULL(name) }, _data{ std::in_place_type<InvokerEntry>, handler, help, permission, allowConsole }
239 {}
240
241 template <typename TypedHandler>
242 ChatCommandBuilder(char const* name, TypedHandler& handler, rbac::RBACPermissions permission, Trinity::ChatCommands::Console allowConsole)
243 : ChatCommandBuilder(name, handler, TrinityStrings(), permission, allowConsole)
244 {}
245 ChatCommandBuilder(char const* name, std::vector<ChatCommandBuilder> const& subCommands)
246 : _name{ ASSERT_NOTNULL(name) }, _data{ std::in_place_type<SubCommandEntry>, subCommands }
247 {}
248
249 [[deprecated("char const* parameters to command handlers are deprecated; convert this to a typed argument handler instead")]]
250 ChatCommandBuilder(char const* name, bool(&handler)(ChatHandler*, char const*), rbac::RBACPermissions permission, Trinity::ChatCommands::Console allowConsole)
251 : ChatCommandBuilder(name, handler, TrinityStrings(), permission, allowConsole)
252 {}
253
254 template <typename TypedHandler>
255 [[deprecated("you are using the old-style command format; convert this to the new format ({ name, handler (not a pointer!), permission, Console::(Yes/No) })")]]
256 ChatCommandBuilder(char const* name, rbac::RBACPermissions permission, bool console, TypedHandler* handler, char const*)
257 : ChatCommandBuilder(name, *handler, TrinityStrings(), permission, static_cast<Trinity::ChatCommands::Console>(console))
258 {}
259
260 [[deprecated("you are using the old-style command format; convert this to the new format ({ name, subCommands })")]]
261 ChatCommandBuilder(char const* name, rbac::RBACPermissions, bool, std::nullptr_t, char const*, std::vector <ChatCommandBuilder> const& sub)
262 : ChatCommandBuilder(name, sub)
263 {}
264
265 private:
266 std::string_view _name;
267 std::variant<InvokerEntry, SubCommandEntry> _data;
268 };
269
272 TC_GAME_API bool TryExecuteCommand(ChatHandler& handler, std::string_view cmd);
273 TC_GAME_API void SendCommandHelpFor(ChatHandler& handler, std::string_view cmd);
274 TC_GAME_API std::vector<std::string> GetAutoCompletionsFor(ChatHandler const& handler, std::string_view cmd);
275}
276
277// backwards compatibility with old patches
278using ChatCommand [[deprecated("std::vector<ChatCommand> should be ChatCommandTable! (using namespace Trinity::ChatCommands)")]] = Trinity::ChatCommands::ChatCommandBuilder;
279
280#endif
#define TC_GAME_API
Definition: Define.h:123
#define ASSERT_NOTNULL(pointer)
Definition: Errors.h:84
#define ASSERT
Definition: Errors.h:68
TrinityStrings
Definition: Language.h:29
@ LANG_CMDPARSER_EITHER
Definition: Language.h:1001
@ LANG_CMDPARSER_OR
Definition: Language.h:1002
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition: Optional.h:25
Role Based Access Control related classes definition.
void SendCommandHelp(ChatHandler &handler) const
std::map< std::string_view, ChatCommandNode, StringCompareLessI_T > _subCommands
Definition: ChatCommand.h:209
std::variant< std::monostate, TrinityStrings, std::string > _help
Definition: ChatCommand.h:208
ChatCommandNode(ChatCommandNode &&other)=default
bool HasVisibleSubCommands(ChatHandler const &who) const
static void SendCommandHelpFor(ChatHandler &handler, std::string_view cmd)
void LoadFromBuilder(ChatCommandBuilder const &builder)
Definition: ChatCommand.cpp:32
Trinity::ChatCommands::ChatCommandBuilder ChatCommandBuilder
Definition: ChatCommand.h:180
static std::map< std::string_view, ChatCommandNode, StringCompareLessI_T > const & GetTopLevelMap()
Definition: ChatCommand.cpp:69
static void LoadCommandsIntoMap(ChatCommandNode *blank, std::map< std::string_view, ChatCommandNode, StringCompareLessI_T > &map, Trinity::ChatCommands::ChatCommandTable const &commands)
Definition: ChatCommand.cpp:47
bool IsVisible(ChatHandler const &who) const
Definition: ChatCommand.h:201
bool IsInvokerVisible(ChatHandler const &who) const
static std::vector< std::string > GetAutoCompletionsFor(ChatHandler const &handler, std::string_view cmd)
static bool TryExecuteCommand(ChatHandler &handler, std::string_view cmd)
void apply(T *val)
Definition: ByteConverter.h:41
TC_GAME_API std::vector< std::string > GetAutoCompletionsFor(ChatHandler const &handler, std::string_view cmd)
TC_GAME_API void SendCommandHelpFor(ChatHandler &handler, std::string_view cmd)
std::vector< ChatCommandBuilder > ChatCommandTable
Definition: ChatCommand.h:49
TC_GAME_API bool TryExecuteCommand(ChatHandler &handler, std::string_view cmd)
TC_GAME_API void InvalidateCommandMap()
TC_GAME_API void LoadCommandMap()
typename HandlerToTuple< T >::type TupleType
Definition: ChatCommand.h:118
TC_GAME_API char const * GetTrinityString(ChatHandler const *handler, TrinityStrings which)
ChatCommandResult ConsumeFromOffset(Tuple &, ChatHandler const *handler, std::string_view args)
Definition: ChatCommand.h:106
TC_GAME_API void SendErrorMessageToHandler(ChatHandler *handler, std::string_view str)
std::string StringFormat(FormatString< Args... > fmt, Args &&... args)
Default TC string format function.
Definition: StringFormat.h:38
RBACPermissions
Definition: RBAC.h:53
STL namespace.
InvokerEntry(T &handler, TrinityStrings help, rbac::RBACPermissions permission, Trinity::ChatCommands::Console allowConsole)
Definition: ChatCommand.h:221
Trinity::Impl::ChatCommands::CommandInvoker _invoker
Definition: ChatCommand.h:227
Trinity::Impl::ChatCommands::CommandPermissions _permissions
Definition: ChatCommand.h:229
std::variant< InvokerEntry, SubCommandEntry > _data
Definition: ChatCommand.h:267
std::reference_wrapper< std::vector< ChatCommandBuilder > const > SubCommandEntry
Definition: ChatCommand.h:231
ChatCommandBuilder(char const *name, std::vector< ChatCommandBuilder > const &subCommands)
Definition: ChatCommand.h:245
ChatCommandBuilder(char const *name, rbac::RBACPermissions permission, bool console, TypedHandler *handler, char const *)
Definition: ChatCommand.h:256
ChatCommandBuilder(ChatCommandBuilder const &)=default
ChatCommandBuilder(char const *name, TypedHandler &handler, TrinityStrings help, rbac::RBACPermissions permission, Trinity::ChatCommands::Console allowConsole)
Definition: ChatCommand.h:237
ChatCommandBuilder(ChatCommandBuilder &&)=default
ChatCommandBuilder(char const *name, rbac::RBACPermissions, bool, std::nullptr_t, char const *, std::vector< ChatCommandBuilder > const &sub)
Definition: ChatCommand.h:261
ChatCommandBuilder(char const *name, TypedHandler &handler, rbac::RBACPermissions permission, Trinity::ChatCommands::Console allowConsole)
Definition: ChatCommand.h:242
ChatCommandBuilder(char const *name, bool(&handler)(ChatHandler *, char const *), rbac::RBACPermissions permission, Trinity::ChatCommands::Console allowConsole)
Definition: ChatCommand.h:250
bool operator()(ChatHandler *chatHandler, std::string_view args) const
Definition: ChatCommand.h:157
bool(void *, ChatHandler *, std::string_view) wrapper_func
Definition: ChatCommand.h:164
CommandInvoker(bool(&handler)(ChatHandler *, char const *))
Definition: ChatCommand.h:144
Trinity::ChatCommands::Console AllowConsole
Definition: ChatCommand.h:174
CommandPermissions(rbac::RBACPermissions perm, Trinity::ChatCommands::Console console)
Definition: ChatCommand.h:172
std::tuple< ChatHandler *, std::remove_cvref_t< Ts >... > type
Definition: ChatCommand.h:117
static ChatCommandResult TryConsumeTo(Tuple &tuple, ChatHandler const *handler, std::string_view args)
Definition: ChatCommand.h:77
static ChatCommandResult TryConsumeTo(Tuple &tuple, ChatHandler const *handler, std::string_view args)
Definition: ChatCommand.h:64