/* * Copyright (C) 2008-2018 TrinityCore * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #ifndef TRINITY_CHATCOMMAND_H #define TRINITY_CHATCOMMAND_H #include "advstd.h" #include "ChatCommandArgs.h" #include "ChatCommandTags.h" #include "Define.h" #include "Errors.h" #include "ObjectGuid.h" #include "Optional.h" #include #include #include #include class ChatHandler; class CommandArgs; template struct CommandArgsConsumerSingle { using arginfo = Trinity::ChatCommands::ArgInfo; static char const* TryConsumeTo(T& val, char const* args) { return arginfo::TryConsume(val, args); } }; struct CommandArgsVariantConsumer { template static std::enable_if_t TryConsumeTo(V& val, char const* args) { T1 v; if (char const* next = CommandArgsConsumerSingle::TryConsumeTo(v, args)) { val = std::move(v); return next; } else return TryConsumeTo(val, args); } template static char const* TryConsumeTo(V& val, char const* args) { T1 v; if (char const* next = CommandArgsConsumerSingle::TryConsumeTo(v, args)) { val = std::move(v); return next; } else return nullptr; } }; template struct CommandArgsConsumerSingle> { static char const* TryConsumeTo(Trinity::ChatCommands::Variant& val, char const* args) { return CommandArgsVariantConsumer::TryConsumeTo, Ts...>(val, args); } }; template struct CommandArgsConsumerSingle> { static char const* TryConsumeTo(std::vector& val, char const* args) { char const* last; val.clear(); do val.emplace_back(); while ((args = CommandArgsConsumerSingle::TryConsumeTo(val.back(), (last = args)))); val.pop_back(); return last; } }; template <> struct CommandArgsConsumerSingle { static char const* TryConsumeTo(CommandArgs*&, char const* args) { return args; } }; template <> struct CommandArgsConsumerSingle { static char const* TryConsumeTo(char const*&, char const* args) { return args; } }; template struct CommandArgsConsumerNext; template struct CommandArgsConsumerMulti { static char const* TryConsumeTo(Tuple& tuple, char const* args) { if (char const* next = CommandArgsConsumerSingle::TryConsumeTo(std::get(tuple), args)) return CommandArgsConsumerNext::GoNext(tuple, next); else return nullptr; } }; template struct CommandArgsConsumerMulti, offset> { static char const* TryConsumeTo(Tuple& tuple, char const* args) { // try with the argument auto& myArg = std::get(tuple); myArg.emplace(); if (char const* next = CommandArgsConsumerSingle::TryConsumeTo(*(myArg.get_ptr()), args)) if ((next = CommandArgsConsumerNext::GoNext(tuple, next))) return next; // try again omitting the argument myArg = boost::none; if (char const* next = CommandArgsConsumerNext::GoNext(tuple, args)) return next; return nullptr; } }; template struct CommandArgsConsumerNext, offset> { using tuple_type = std::tuple; template static std::enable_if_t GoNext(tuple_type& tuple, char const* args) { return CommandArgsConsumerMulti, offset>::TryConsumeTo(tuple, args); } template static std::enable_if_t GoNext(tuple_type&, char const* args) { return args; } }; class TC_GAME_API CommandArgs { public: CommandArgs(char const* args) : _original(args), _args(args) {} template auto TryConsume() { Optional, advstd::remove_cvref_t, advstd::remove_cvref_t...>> rv; rv.emplace(); if (!TryConsumeToTuple<0>(*(rv.get_ptr()))) rv = boost::none; return rv; } template auto TryConsume() { using T = advstd::remove_cvref_t; Optional rv; rv.emplace(); if (char const* next = CommandArgsConsumerSingle::TryConsumeTo(*(rv.get_ptr()), _args)) _args = next; else rv = boost::none; return rv; } template bool TryConsumeToTuple(T& tuple) { if (char const* next = CommandArgsConsumerNext::GoNext(tuple, _args)) { _args = next; return true; } else return false; } 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 struct ChatCommandHandlerToTuple { static_assert(!advstd::is_same_v, "Invalid command handler signature"); }; template struct ChatCommandHandlerToTuple { using type = std::tuple...>; }; template struct ChatCommandStoreLastArg { static void store(T&, CommandArgs&) {} }; template <> struct ChatCommandStoreLastArg { static void store(char const*& arg, CommandArgs& args) { arg = args.GetRemainingArgs(); } }; template <> struct ChatCommandStoreLastArg { static void store(CommandArgs*& arg, CommandArgs& args) { arg = &args; } }; class TC_GAME_API ChatCommand { using wrapper_func = bool(void*, ChatHandler*, char const*); public: template ChatCommand(char const* name, uint32 permission, bool allowConsole, TypedHandler handler, std::string help) : Name(ASSERT_NOTNULL(name)), Permission(permission), AllowConsole(allowConsole), Help(std::move(help)), ChildCommands({}) { _wrapper = [](void* handler, ChatHandler* chatHandler, char const* argsStr) { using tuple_type = typename ChatCommandHandlerToTuple::type; tuple_type arguments; std::get<0>(arguments) = chatHandler; CommandArgs args(argsStr); if (args.TryConsumeToTuple<1>(arguments)) { auto& last = std::get-1>(arguments); ChatCommandStoreLastArg>::store(last, args); return advstd::apply(reinterpret_cast(handler), std::move(arguments)); } else return false; }; _handler = reinterpret_cast(handler); } ChatCommand(char const* name, uint32 permission, bool allowConsole, std::nullptr_t, std::string help, std::vector childCommands = {}) : Name(ASSERT_NOTNULL(name)), Permission(permission), AllowConsole(allowConsole), Help(std::move(help)), ChildCommands(std::move(childCommands)) { _wrapper = nullptr; _handler = nullptr; } bool operator()(ChatHandler* chatHandler, char const* args) const { ASSERT(_wrapper && _handler); return _wrapper(_handler, chatHandler, args); } bool HasHandler() const { return !!_handler; } char const* Name; uint32 Permission; // function pointer required correct align (use uint32) bool AllowConsole; std::string Help; std::vector ChildCommands; private: wrapper_func* _wrapper; void* _handler; }; #endif