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 Refs = std::tuple<Ts...>;
116 using Vals = std::tuple<std::remove_cvref_t<Ts>...>;
117
118 static bool Wrapper(void* handler, ChatHandler* chatHandler, std::string_view argsStr) noexcept
119 {
120 Vals arguments;
121 ChatCommandResult result = std::nullopt;
122 ConsumeFromOffset<Vals, 0>(result, arguments, chatHandler, argsStr);
123 if (result.IsSuccessful())
124 return Invoke(reinterpret_cast<Func*>(handler), chatHandler, arguments, std::make_index_sequence<std::tuple_size_v<Vals>>{});
125
126 if (result.HasErrorMessage())
127 SendErrorMessageToHandler(chatHandler, result.GetErrorMessage());
128
129 return false;
130 }
131
132 template <std::size_t... I>
133 inline static constexpr bool Invoke(Func* handler, ChatHandler* chatHandler, Vals& arguments, std::index_sequence<I...>) noexcept
134 {
135 // Invoke command handler preserving original reference category of each argument
136 return handler(chatHandler, std::get<I>(advstd::forward_like<std::tuple_element_t<I, Refs>>(arguments))...);
137 }
138 };
139
140 template <typename... Ts> struct CommandInvokerTraits<bool(ChatHandler const*, Ts...)> : CommandInvokerTraits<bool(ChatHandler*, Ts...)> { };
141
143 {
144 CommandInvoker() = default;
145 template <typename TypedHandler>
146 CommandInvoker(TypedHandler& handler) : _wrapper(&CommandInvokerTraits<TypedHandler>::Wrapper), _handler(reinterpret_cast<void*>(handler)) { }
147 CommandInvoker(bool(&handler)(ChatHandler*, char const*)) : _wrapper(&LegacyWrapper), _handler(reinterpret_cast<void*>(handler)) { }
148
149 explicit operator bool() const { return (_wrapper != nullptr); }
150 bool operator()(ChatHandler* chatHandler, std::string_view args) const
151 {
153 return _wrapper(_handler, chatHandler, args);
154 }
155
156 private:
157 static bool LegacyWrapper(void* handler, ChatHandler* chatHandler, std::string_view argsStr) noexcept
158 {
159 // make a copy of the argument string
160 // legacy handlers can destroy input strings with strtok
161 std::string argsStrCopy(argsStr);
162 return reinterpret_cast<bool(*)(ChatHandler*, char const*)>(handler)(chatHandler, argsStrCopy.c_str());
163 }
164
165 using wrapper_func = std::add_pointer_t<bool(void*, ChatHandler*, std::string_view) noexcept>;
167 void* _handler = nullptr;
168 };
169
177
179 {
182
183 public:
184 static void LoadCommandMap();
185 static void InvalidateCommandMap();
186 static bool TryExecuteCommand(ChatHandler& handler, std::string_view cmd);
187 static void SendCommandHelpFor(ChatHandler& handler, std::string_view cmd);
188 static std::vector<std::string> GetAutoCompletionsFor(ChatHandler const& handler, std::string_view cmd);
189
190 ChatCommandNode() = default;
191 ~ChatCommandNode() = default;
192
193 private:
194 static std::map<std::string_view, ChatCommandNode, StringCompareLessI_T> const& GetTopLevelMap();
195 static void LoadCommandsIntoMap(ChatCommandNode* blank, std::map<std::string_view, ChatCommandNode, StringCompareLessI_T>& map, std::span<ChatCommandBuilder const> commands);
196
197 void LoadFromBuilder(ChatCommandBuilder const& builder);
198 ChatCommandNode(ChatCommandNode const& other) = delete;
199 ChatCommandNode(ChatCommandNode&& other) noexcept = default;
201 ChatCommandNode& operator=(ChatCommandNode&& other) noexcept = default;
202
203 void ResolveNames(std::string name);
204 void SendCommandHelp(ChatHandler& handler) const;
205
206 bool IsVisible(ChatHandler const& who) const { return (IsInvokerVisible(who) || HasVisibleSubCommands(who)); }
207 bool IsInvokerVisible(ChatHandler const& who) const;
208 bool HasVisibleSubCommands(ChatHandler const& who) const;
209
210 std::string _name;
213 std::variant<std::monostate, TrinityStrings, std::string> _help;
214 std::map<std::string_view, ChatCommandNode, StringCompareLessI_T> _subCommands;
215 };
216}
217
218namespace Trinity::ChatCommands
219{
221 {
234 using SubCommandEntry = std::span<ChatCommandBuilder const>;
235
236 template <typename TypedHandler>
237 ChatCommandBuilder(std::string_view name, TypedHandler& handler, TrinityStrings help, rbac::RBACPermissions permission, Trinity::ChatCommands::Console allowConsole)
238 : _name{ name }, _data{ InvokerEntry { handler, help, permission, allowConsole } }
239 {}
240
241 template <typename TypedHandler>
242 ChatCommandBuilder(std::string_view name, TypedHandler& handler, rbac::RBACPermissions permission, Trinity::ChatCommands::Console allowConsole)
243 : ChatCommandBuilder(name, handler, TrinityStrings(), permission, allowConsole)
244 {}
245 ChatCommandBuilder(std::string_view name, std::span<ChatCommandBuilder const> subCommands)
246 : _name{ name }, _data{ std::in_place_index<1>, subCommands.data(), subCommands.size() }
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*, SubCommandEntry sub)
262 : ChatCommandBuilder(name, sub)
263 {}
264
265 private:
266 std::string_view _name;
267 std::variant<InvokerEntry, std::pair<ChatCommandBuilder const*, std::size_t> /*workaround: span requires type to be complete*/> _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#endif
#define TC_GAME_API
Definition Define.h:129
#define ASSERT
Definition Errors.h:80
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)
constexpr decltype(auto) forward_like(U &&value) noexcept
Definition advstd.h:66
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