Files
Viano-Core/src/server/game/Chat/ChatCommands/ChatCommand.h
T
Aokromes 6815c24be1 Scripts/Commands: New argument parsing methodology (PR #22363)
- Detect the arguments accepted by the command handler
- Tokenize out those arguments automatically and feed them to the handler
- Unmatched rest of the string can be accepted by trailing char const* or CommandArgs*
2018-09-09 14:31:14 +02:00

280 lines
9.3 KiB
C++

/*
* Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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 <cstddef>
#include <tuple>
#include <type_traits>
#include <vector>
class ChatHandler;
class CommandArgs;
template <typename T>
struct CommandArgsConsumerSingle
{
using arginfo = Trinity::ChatCommands::ArgInfo<T>;
static char const* TryConsumeTo(T& val, char const* args)
{
return arginfo::TryConsume(val, args);
}
};
struct CommandArgsVariantConsumer
{
template <typename V, typename T1, typename... Ts>
static std::enable_if_t<sizeof...(Ts), char const*> TryConsumeTo(V& val, char const* args)
{
T1 v;
if (char const* next = CommandArgsConsumerSingle<T1>::TryConsumeTo(v, args))
{
val = std::move(v);
return next;
}
else
return TryConsumeTo<V, Ts...>(val, args);
}
template <typename V, typename T1>
static char const* TryConsumeTo(V& val, char const* args)
{
T1 v;
if (char const* next = CommandArgsConsumerSingle<T1>::TryConsumeTo(v, args))
{
val = std::move(v);
return next;
}
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)
{
char const* last;
val.clear();
do val.emplace_back();
while ((args = CommandArgsConsumerSingle<T>::TryConsumeTo(val.back(), (last = args))));
val.pop_back();
return last;
}
};
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>::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)
{
// try with the argument
auto& myArg = std::get<offset>(tuple);
myArg.emplace();
if (char const* next = CommandArgsConsumerSingle<NestedNextType>::TryConsumeTo(*(myArg.get_ptr()), args))
if ((next = CommandArgsConsumerNext<Tuple, offset>::GoNext(tuple, next)))
return next;
// try again omitting the argument
myArg = boost::none;
if (char const* next = CommandArgsConsumerNext<Tuple, offset>::GoNext(tuple, args))
return next;
return nullptr;
}
};
template <size_t offset, typename... Ts>
struct CommandArgsConsumerNext<std::tuple<Ts...>, offset>
{
using tuple_type = std::tuple<Ts...>;
template <bool C = (offset + 1 < 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 + 1, tuple_type>, offset + 1>::TryConsumeTo(tuple, args);
}
template <bool C = (offset + 1 < sizeof...(Ts))>
static std::enable_if_t<!C, char const*> GoNext(tuple_type&, char const* args)
{
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()
{
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.get_ptr())))
rv = boost::none;
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.get_ptr()), _args))
_args = next;
else
rv = boost::none;
return rv;
}
template <size_t offset = 0, typename T>
bool TryConsumeToTuple(T& tuple)
{
if (char const* next = CommandArgsConsumerMulti<T, std::tuple_element_t<offset, T>, offset>::TryConsumeTo(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 <typename T> struct ChatCommandHandlerToTuple { static_assert(!advstd::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 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
{
using wrapper_func = bool(void*, ChatHandler*, char const*);
public:
template <typename TypedHandler>
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<TypedHandler>::type;
tuple_type arguments;
std::get<0>(arguments) = chatHandler;
CommandArgs args(argsStr);
if (args.TryConsumeToTuple<1>(arguments))
{
auto& last = std::get<advstd::tuple_size_v<tuple_type>-1>(arguments);
ChatCommandStoreLastArg<advstd::remove_cvref_t<decltype(last)>>::store(last, args);
return advstd::apply(reinterpret_cast<TypedHandler>(handler), std::move(arguments));
}
else
return false;
};
_handler = reinterpret_cast<void*>(handler);
}
ChatCommand(char const* name, uint32 permission, bool allowConsole, std::nullptr_t, std::string help, std::vector<ChatCommand> 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<ChatCommand> ChildCommands;
private:
wrapper_func* _wrapper;
void* _handler;
};
#endif