TrinityCore
Loading...
Searching...
No Matches
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;
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 void ConsumeFromOffset(ChatCommandResult& result, Tuple&, ChatHandler const* handler, std::string_view args) noexcept;
60
61 template <typename Tuple, typename NextType, size_t offset>
63 {
64 inline static ChatCommandResult TryConsumeTo(Tuple& tuple, ChatHandler const* handler, std::string_view args) noexcept
65 {
66 ChatCommandResult next = ArgInfo<NextType>::TryConsume(std::get<offset>(tuple), handler, args);
67 if (next)
68 ConsumeFromOffset<Tuple, offset + 1>(next, tuple, handler, *next);
69 return next;
70 }
71 };
72
73 TC_GAME_API void MergeChatCommandResults(ChatHandler const* handler, ChatCommandResult& result1, ChatCommandResult& result2) noexcept;
74
75 template <typename Tuple, typename NestedNextType, size_t offset>
76 struct MultiConsumer<Tuple, Optional<NestedNextType>, offset>
77 {
78 static ChatCommandResult TryConsumeTo(Tuple& tuple, ChatHandler const* handler, std::string_view args) noexcept
79 {
80 // try with the argument
81 auto& myArg = std::get<offset>(tuple);
82
83 ChatCommandResult result1 = ArgInfo<NestedNextType>::TryConsume(myArg.emplace(), handler, args);
84 if (result1)
85 {
86 ConsumeFromOffset<Tuple, offset + 1>(result1, tuple, handler, *result1);
87 if (result1)
88 return result1;
89 }
90
91 // try again omitting the argument
92 myArg.reset();
93 ChatCommandResult result2 = std::nullopt;
94 ConsumeFromOffset<Tuple, offset + 1>(result2, tuple, handler, args);
95 MergeChatCommandResults(handler, result1, result2);
96 return result1;
97 }
98 };
99
100 template <typename Tuple, size_t offset>
101 void ConsumeFromOffset([[maybe_unused]] ChatCommandResult& result, [[maybe_unused]] Tuple& tuple, [[maybe_unused]] ChatHandler const* handler, std::string_view args) noexcept
102 {
103 if constexpr (offset < std::tuple_size_v<Tuple>)
104 result = MultiConsumer<Tuple, std::tuple_element_t<offset, Tuple>, offset>::TryConsumeTo(tuple, handler, args);
105 else if (!args.empty()) /* the entire string must be consumed */
106 result = std::nullopt;
107 else
108 result = args;
109 }
110
111 template <typename T> struct CommandInvokerTraits { static_assert(Trinity::dependant_false_v<T>, "Invalid command handler signature"); };
112 template <typename... Ts> struct CommandInvokerTraits<bool(ChatHandler*, Ts...)>
113 {
114 using Func = bool(ChatHandler*, Ts...);
115 using Vals = std::tuple<std::remove_cvref_t<Ts>...>;
116
117 static bool Wrapper(void* handler, ChatHandler* chatHandler, std::string_view argsStr) noexcept
118 {
119 Vals arguments;
120 ChatCommandResult result = std::nullopt;
121 ConsumeFromOffset<Vals, 0>(result, arguments, chatHandler, argsStr);
122 if (result.IsSuccessful())
123 return Invoke(reinterpret_cast<Func*>(handler), chatHandler, arguments, std::index_sequence_for<Ts...>{});
124
125 if (result.HasErrorMessage())
126 SendErrorMessageToHandler(chatHandler, result.GetErrorMessage());
127
128 return false;
129 }
130
131 template <std::size_t... I>
132 inline static constexpr bool Invoke(Func* handler, ChatHandler* chatHandler, Vals& arguments, std::index_sequence<I...>) noexcept
133 {
134 // Invoke command handler preserving original reference category of each argument
135 return handler(chatHandler, std::get<I>(advstd::forward_like<Ts>(arguments))...);
136 }
137 };
138
139 template <typename... Ts> struct CommandInvokerTraits<bool(ChatHandler const*, Ts...)> : CommandInvokerTraits<bool(ChatHandler*, Ts...)> { };
140
142 {
143 CommandInvoker() = default;
144 template <typename TypedHandler>
145 CommandInvoker(TypedHandler& handler) : _wrapper(&CommandInvokerTraits<TypedHandler>::Wrapper), _handler(reinterpret_cast<void*>(handler)) { }
146 CommandInvoker(bool(&handler)(ChatHandler*, char const*)) : _wrapper(&LegacyWrapper), _handler(reinterpret_cast<void*>(handler)) { }
147
148 explicit operator bool() const { return (_wrapper != nullptr); }
149 bool operator()(ChatHandler* chatHandler, std::string_view args) const
150 {
152 return _wrapper(_handler, chatHandler, args);
153 }
154
155 private:
156 static bool LegacyWrapper(void* handler, ChatHandler* chatHandler, std::string_view argsStr) noexcept
157 {
158 // make a copy of the argument string
159 // legacy handlers can destroy input strings with strtok
160 std::string argsStrCopy(argsStr);
161 return reinterpret_cast<bool(*)(ChatHandler*, char const*)>(handler)(chatHandler, argsStrCopy.c_str());
162 }
163
164 using wrapper_func = std::add_pointer_t<bool(void*, ChatHandler*, std::string_view) noexcept>;
166 void* _handler = nullptr;
167 };
168
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
189 ChatCommandNode() = default;
190 ~ChatCommandNode() = default;
191
192 private:
193 static std::map<std::string_view, ChatCommandNode, StringCompareLessI_T> const& GetTopLevelMap();
194 static void LoadCommandsIntoMap(ChatCommandNode* blank, std::map<std::string_view, ChatCommandNode, StringCompareLessI_T>& map, std::span<ChatCommandBuilder const> commands);
195
196 void LoadFromBuilder(ChatCommandBuilder const& builder);
197 ChatCommandNode(ChatCommandNode const& other) = delete;
198 ChatCommandNode(ChatCommandNode&& other) noexcept = default;
200 ChatCommandNode& operator=(ChatCommandNode&& other) noexcept = default;
201
202 void ResolveNames(std::string name);
203 void SendCommandHelp(ChatHandler& handler) const;
204
205 bool IsVisible(ChatHandler const& who) const { return (IsInvokerVisible(who) || HasVisibleSubCommands(who)); }
206 bool IsInvokerVisible(ChatHandler const& who) const;
207 bool HasVisibleSubCommands(ChatHandler const& who) const;
208
209 std::string _name;
212 std::variant<std::monostate, TrinityStrings, std::string> _help;
213 std::map<std::string_view, ChatCommandNode, StringCompareLessI_T> _subCommands;
214 };
215}
216
217namespace Trinity::ChatCommands
218{
220 {
233 using SubCommandEntry = std::span<ChatCommandBuilder const>;
234
235 template <typename TypedHandler>
236 ChatCommandBuilder(std::string_view name, TypedHandler& handler, TrinityStrings help, rbac::RBACPermissions permission, Trinity::ChatCommands::Console allowConsole)
237 : _name{ name }, _data{ InvokerEntry { handler, help, permission, allowConsole } }
238 {}
239
240 template <typename TypedHandler>
241 ChatCommandBuilder(std::string_view name, TypedHandler& handler, rbac::RBACPermissions permission, Trinity::ChatCommands::Console allowConsole)
242 : ChatCommandBuilder(name, handler, TrinityStrings(), permission, allowConsole)
243 {}
244 ChatCommandBuilder(std::string_view name, std::span<ChatCommandBuilder const> subCommands)
245 : _name{ name }, _data{ std::in_place_index<1>, subCommands.data(), subCommands.size() }
246 {}
247
248 [[deprecated("char const* parameters to command handlers are deprecated; convert this to a typed argument handler instead")]]
249 ChatCommandBuilder(char const* name, bool(&handler)(ChatHandler*, char const*), rbac::RBACPermissions permission, Trinity::ChatCommands::Console allowConsole)
250 : ChatCommandBuilder(name, handler, TrinityStrings(), permission, allowConsole)
251 {}
252
253 template <typename TypedHandler>
254 [[deprecated("you are using the old-style command format; convert this to the new format ({ name, handler (not a pointer!), permission, Console::(Yes/No) })")]]
255 ChatCommandBuilder(char const* name, rbac::RBACPermissions permission, bool console, TypedHandler* handler, char const*)
256 : ChatCommandBuilder(name, *handler, TrinityStrings(), permission, static_cast<Trinity::ChatCommands::Console>(console))
257 {}
258
259 [[deprecated("you are using the old-style command format; convert this to the new format ({ name, subCommands })")]]
260 ChatCommandBuilder(char const* name, rbac::RBACPermissions, bool, std::nullptr_t, char const*, SubCommandEntry sub)
261 : ChatCommandBuilder(name, sub)
262 {}
263
264 private:
265 std::string_view _name;
266 std::variant<InvokerEntry, std::pair<ChatCommandBuilder const*, std::size_t> /*workaround: span requires type to be complete*/> _data;
267 };
268
271 TC_GAME_API bool TryExecuteCommand(ChatHandler& handler, std::string_view cmd);
272 TC_GAME_API void SendCommandHelpFor(ChatHandler& handler, std::string_view cmd);
273 TC_GAME_API std::vector<std::string> GetAutoCompletionsFor(ChatHandler const& handler, std::string_view cmd);
274}
275
276#endif
#define TC_GAME_API
Definition Define.h:129
#define ASSERT
Definition Errors.h:72
TrinityStrings
Definition Language.h:29
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
ChatCommandNode(ChatCommandNode const &other)=delete
static void LoadCommandsIntoMap(ChatCommandNode *blank, std::map< std::string_view, ChatCommandNode, StringCompareLessI_T > &map, std::span< ChatCommandBuilder const > commands)
ChatCommandNode & operator=(ChatCommandNode &&other) noexcept=default
std::map< std::string_view, ChatCommandNode, StringCompareLessI_T > _subCommands
std::variant< std::monostate, TrinityStrings, std::string > _help
ChatCommandNode(ChatCommandNode &&other) noexcept=default
bool HasVisibleSubCommands(ChatHandler const &who) const
static void SendCommandHelpFor(ChatHandler &handler, std::string_view cmd)
void LoadFromBuilder(ChatCommandBuilder const &builder)
static std::map< std::string_view, ChatCommandNode, StringCompareLessI_T > const & GetTopLevelMap()
bool IsVisible(ChatHandler const &who) const
ChatCommandNode & operator=(ChatCommandNode const &other)=delete
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)
TC_GAME_API std::vector< std::string > GetAutoCompletionsFor(ChatHandler const &handler, std::string_view cmd)
ChatCommandBuilder const [] ChatCommandTable
Definition ChatCommand.h:49
TC_GAME_API void SendCommandHelpFor(ChatHandler &handler, std::string_view cmd)
TC_GAME_API bool TryExecuteCommand(ChatHandler &handler, std::string_view cmd)
TC_GAME_API void InvalidateCommandMap()
TC_GAME_API void LoadCommandMap()
TC_GAME_API void MergeChatCommandResults(ChatHandler const *handler, ChatCommandResult &result1, ChatCommandResult &result2) noexcept
void ConsumeFromOffset(ChatCommandResult &result, Tuple &, ChatHandler const *handler, std::string_view args) noexcept
TC_GAME_API void SendErrorMessageToHandler(ChatHandler *handler, std::string_view str)
RBACPermissions
Definition RBAC.h:53
STL namespace.
InvokerEntry(T &handler, TrinityStrings help, rbac::RBACPermissions permission, Trinity::ChatCommands::Console allowConsole)
Trinity::Impl::ChatCommands::CommandInvoker _invoker
Trinity::Impl::ChatCommands::CommandPermissions _permissions
ChatCommandBuilder(std::string_view name, TypedHandler &handler, TrinityStrings help, rbac::RBACPermissions permission, Trinity::ChatCommands::Console allowConsole)
ChatCommandBuilder(char const *name, rbac::RBACPermissions permission, bool console, TypedHandler *handler, char const *)
ChatCommandBuilder(std::string_view name, TypedHandler &handler, rbac::RBACPermissions permission, Trinity::ChatCommands::Console allowConsole)
ChatCommandBuilder(char const *name, rbac::RBACPermissions, bool, std::nullptr_t, char const *, SubCommandEntry sub)
ChatCommandBuilder(std::string_view name, std::span< ChatCommandBuilder const > subCommands)
std::span< ChatCommandBuilder const > SubCommandEntry
ChatCommandBuilder(char const *name, bool(&handler)(ChatHandler *, char const *), rbac::RBACPermissions permission, Trinity::ChatCommands::Console allowConsole)
std::variant< InvokerEntry, std::pair< ChatCommandBuilder const *, std::size_t > > _data
static constexpr bool Invoke(Func *handler, ChatHandler *chatHandler, Vals &arguments, std::index_sequence< I... >) noexcept
static bool Wrapper(void *handler, ChatHandler *chatHandler, std::string_view argsStr) noexcept
std::add_pointer_t< bool(void *, ChatHandler *, std::string_view) noexcept > wrapper_func
static bool LegacyWrapper(void *handler, ChatHandler *chatHandler, std::string_view argsStr) noexcept
bool operator()(ChatHandler *chatHandler, std::string_view args) const
CommandInvoker(bool(&handler)(ChatHandler *, char const *))
Trinity::ChatCommands::Console AllowConsole
CommandPermissions(rbac::RBACPermissions perm, Trinity::ChatCommands::Console console)
static ChatCommandResult TryConsumeTo(Tuple &tuple, ChatHandler const *handler, std::string_view args) noexcept
Definition ChatCommand.h:78
static ChatCommandResult TryConsumeTo(Tuple &tuple, ChatHandler const *handler, std::string_view args) noexcept
Definition ChatCommand.h:64