/*
* 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