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*
This commit is contained in:
Treeston
2018-09-07 20:31:04 +02:00
committed by GitHub
parent 5843724deb
commit 66a87c4642
14 changed files with 1041 additions and 384 deletions

View File

@@ -0,0 +1,33 @@
--
DELETE FROM `command` WHERE `name` IN ('go creature', 'go creature id', 'go object', 'go gameobject', 'go gameobject id', 'go graveyard', 'go grid', 'go instance', 'go offset', 'go taxinode', 'go ticket', 'go trigger', 'go areatrigger', 'go xyz', 'go zonexy');
INSERT INTO `command` (`name`,`permission`,`help`) VALUES
('go creature', 377, 'Syntax: .go creature <spawnId>
Teleport yourself to the creature spawn with the specified spawn ID.'),
('go creature id', 377, 'Syntax: .go creature id <creatureId>
Teleport yourself to the first spawn point for the specified creature ID.
If multiple spawn points for the creature exist, teleport to the first one found.'),
('go gameobject', 377, 'Syntax: .go gameobject <spawnId>
Teleport yourself to the gameobject spawn with the specified spawn ID.'),
('go gameobject id', 377, 'Syntax: .go gameobject id <goId>
Teleport yourself to the first spawn point for the specified gameobject ID.
If multiple spawn points for the gameobject exist, teleport to the first one found.'),
('go graveyard', 377, 'Syntax: .go graveyard <graveyardId>
Teleport yourself to the graveyard with the specified graveyard ID.'),
('go grid', 377, 'Syntax: .go grid <gridX> <gridY> [<mapId>]
Teleport yourself to center of grid at the provided indices in specified (or current) map.'),
('go instance', 377, 'Syntax: .go instance <part of scriptname>
Teleport yourself to the entrance of the instance matching the specified script name.'),
('go offset', 377, 'Syntax: .go offset [<dForward> [<dSideways> [<dZ [<dO>]]]]
Teleport yourself by the specified amount relative to your current position and orientation.'),
('go taxinode', 377, 'Syntax: .go taxinode <nodeId>
Teleport yourself to the specified taxi node.'),
('go ticket', 377, 'Syntax: .go ticket <ticketId>
Teleport yourself to the location at which the specified ticket was created.'),
('go areatrigger', 377, 'Syntax: .go areatrigger <areatriggerId>
Teleport yourself to the specified areatrigger\'s location.
Note that you may end up at the trigger\'s target location if it is a teleport trigger.'),
('go xyz', 377, 'Syntax: .go xyz <x> <y> [<z> [<mapid> [<o>]]]
Teleport yourself to the specified location in the specified (or current) map.
If no z coordinate is specified, defaults to ground/water level.'),
('go zonexy', 377, 'Syntax: .go zonexy <x> <y> [<zone>]
Teleport yourself to the given local (x,y) position in the specified (or current) zone.');

View File

@@ -22,6 +22,8 @@
#include <utf8.h>
#include <algorithm>
#include <sstream>
#include <string>
#include <cctype>
#include <cstdarg>
#include <ctime>
@@ -573,3 +575,9 @@ bool StringToBool(std::string const& str)
std::transform(str.begin(), str.end(), lowerStr.begin(), ::tolower);
return lowerStr == "1" || lowerStr == "true" || lowerStr == "yes";
}
bool StringContainsStringI(std::string const& haystack, std::string const& needle)
{
return haystack.end() !=
std::search(haystack.begin(), haystack.end(), needle.begin(), needle.end(), [](char c1, char c2) { return std::toupper(c1) == std::toupper(c2); });
}

View File

@@ -300,6 +300,8 @@ TC_COMMON_API void HexStrToByteArray(std::string const& str, uint8* out, bool re
TC_COMMON_API bool StringToBool(std::string const& str);
TC_COMMON_API bool StringContainsStringI(std::string const& haystack, std::string const& needle);
// simple class for not-modifyable list
template <typename T>
class HookList final

View File

@@ -0,0 +1,77 @@
/*
* 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_ADVSTD_H
#define TRINITY_ADVSTD_H
#include <cstddef>
#include <tuple>
#include <type_traits>
#include <utility>
// this namespace holds implementations of upcoming stdlib features that our c++ version doesn't have yet
namespace advstd {
// C++17 std::apply (constrained to only function pointers, not all callable)
template <typename... Ts>
using apply_tuple_type = std::tuple<std::remove_cv_t<std::remove_reference_t<Ts>>...>;
template <typename R, typename... Ts, std::size_t... I>
R apply_impl(R(*func)(Ts...), apply_tuple_type<Ts...>&& args, std::index_sequence<I...>)
{
return func(std::get<I>(std::forward<apply_tuple_type<Ts...>>(args))...);
}
template <typename R, typename... Ts>
R apply(R(*func)(Ts...), apply_tuple_type<Ts...>&& args)
{
return apply_impl(func, std::forward<apply_tuple_type<Ts...>>(args), std::index_sequence_for<Ts...>{});
}
#define forward_1v(stdname, type) template <typename T> constexpr type stdname ## _v = std::stdname<T>::value
#define forward_2v(stdname, type) template <typename U, typename V> constexpr type stdname ## _v = std::stdname<U,V>::value
// C++17 std::is_same_v
forward_2v(is_same, bool);
// C++17 std::is_integral_v
forward_1v(is_integral, bool);
// C++17 std::is_assignable_v
forward_2v(is_assignable, bool);
// C++17 std::is_signed_v
forward_1v(is_signed, bool);
// C++17 std::is_unsigned_v
forward_1v(is_unsigned, bool);
// C++17 std::is_base_of_v
forward_2v(is_base_of, bool);
// C++17 std::is_floating_point_v
forward_1v(is_floating_point, bool);
// C++17 std::tuple_size_v
forward_1v(tuple_size, size_t);
#undef forward_1v
#undef forward_2v
// C++20 std::remove_cvref_t
template <class T>
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
}
#endif

View File

@@ -35,11 +35,6 @@
#include "World.h"
#include <boost/algorithm/string/replace.hpp>
ChatCommand::ChatCommand(char const* name, uint32 permission, bool allowConsole, pHandler handler, std::string help, std::vector<ChatCommand> childCommands /*= std::vector<ChatCommand>()*/)
: Name(ASSERT_NOTNULL(name)), Permission(permission), AllowConsole(allowConsole), Handler(handler), Help(std::move(help)), ChildCommands(std::move(childCommands))
{
}
// Lazy loading of the command table cache from commands and the
// ScriptMgr should be thread safe since the player commands,
// cli commands and ScriptMgr updates are all dispatched one after
@@ -306,12 +301,12 @@ bool ChatHandler::ExecuteCommandInTable(std::vector<ChatCommand> const& table, c
}
// must be available and have handler
if (!table[i].Handler || !isAvailable(table[i]))
if (!table[i].HasHandler() || !isAvailable(table[i]))
continue;
SetSentErrorMessage(false);
// table[i].Name == "" is special case: send original command to handler
if ((table[i].Handler)(this, table[i].Name[0] != '\0' ? text : oldtext))
if (table[i](this, table[i].Name[0] != '\0' ? text : oldtext))
{
if (!m_session) // ignore console
return true;

View File

@@ -20,6 +20,7 @@
#define TRINITYCORE_CHAT_H
#include "Common.h"
#include "ChatCommand.h"
#include "ObjectGuid.h"
#include "SharedDefines.h"
#include "StringFormat.h"
@@ -37,21 +38,6 @@ class WorldPacket;
struct GameTele;
class TC_GAME_API ChatCommand
{
typedef bool(*pHandler)(ChatHandler*, char const*);
public:
ChatCommand(char const* name, uint32 permission, bool allowConsole, pHandler handler, std::string help, std::vector<ChatCommand> childCommands = std::vector<ChatCommand>());
char const* Name;
uint32 Permission; // function pointer required correct align (use uint32)
bool AllowConsole;
pHandler Handler;
std::string Help;
std::vector<ChatCommand> ChildCommands;
};
class TC_GAME_API ChatHandler
{
public:

View File

@@ -0,0 +1,279 @@
/*
* 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

View File

@@ -0,0 +1,37 @@
/*
* 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/>.
*/
#include "ChatCommandArgs.h"
#include "ChatCommand.h"
#include "ChatCommandHyperlinks.h"
#include "ObjectMgr.h"
using namespace Trinity::ChatCommands;
struct GameTeleVisitor
{
using value_type = GameTele const*;
value_type operator()(Hyperlink<tele> tele) const { return sObjectMgr->GetGameTele(tele); }
value_type operator()(std::string const& tele) const { return sObjectMgr->GetGameTele(tele); }
};
char const* Trinity::ChatCommands::ArgInfo<GameTele const*>::TryConsume(GameTele const*& data, char const* args)
{
Variant<Hyperlink<tele>, std::string> val;
if ((args = CommandArgsConsumerSingle<decltype(val)>::TryConsumeTo(val, args)))
data = boost::apply_visitor(GameTeleVisitor(), val);
return args;
}

View File

@@ -0,0 +1,120 @@
/*
* 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_CHATCOMMANDARGS_H
#define TRINITY_CHATCOMMANDARGS_H
#include "ChatCommandHelpers.h"
#include "ChatCommandTags.h"
#include "ChatCommandHyperlinks.h"
struct GameTele;
namespace Trinity {
namespace ChatCommands {
/************************** ARGUMENT HANDLERS *******************************************\
|* Define how to extract contents of a certain requested type from a string *|
|* Must implement the following: *|
|* - TryConsume: T&, char const* -> char const* *|
|* returns nullptr if no match, otherwise pointer to first character of next token *|
|* - if nullptr is returned, state of T& is indeterminate *|
|* - otherwise, T& should be initialized to the intended return value *|
|* *|
\****************************************************************************************/
template <typename T, typename = void>
struct ArgInfo { static_assert(!advstd::is_same_v<T,T>, "Invalid command parameter type - see ChatCommandArgs.h for possible types"); };
// catch-all for signed integral types
template <typename T>
struct ArgInfo<T, std::enable_if_t<advstd::is_integral_v<T> && advstd::is_signed_v<T>>>
{
static char const* TryConsume(T& val, char const* args)
{
char const* next = args;
std::string token(args, tokenize(next));
try { val = std::stoll(token); }
catch (...) { return nullptr; }
return next;
}
};
// catch-all for unsigned integral types
template <typename T>
struct ArgInfo<T, std::enable_if_t<advstd::is_integral_v<T> && advstd::is_unsigned_v<T>>>
{
static char const* TryConsume(T& val, char const* args)
{
char const* next = args;
std::string token(args, tokenize(next));
try { val = std::stoull(token); }
catch (...) { return nullptr; }
return next;
}
};
// catch-all for floating point types
template <typename T>
struct ArgInfo<T, std::enable_if_t<advstd::is_floating_point_v<T>>>
{
static char const* TryConsume(T& val, char const* args)
{
char const* next = args;
std::string token(args, tokenize(next));
try { val = std::stold(token); }
catch (...) { return nullptr; }
return std::isfinite(val) ? next : nullptr;
}
};
// string
template <>
struct ArgInfo<std::string, void>
{
static char const* TryConsume(std::string& val, char const* args)
{
char const* next = args;
if (size_t len = tokenize(next))
{
val.assign(args, len);
return next;
}
else
return nullptr;
}
};
// a container tag
template <typename T>
struct ArgInfo<T, std::enable_if_t<advstd::is_base_of_v<ContainerTag, T>>>
{
static char const* TryConsume(T& tag, char const* args)
{
return tag.TryConsume(args);
}
};
// GameTele* from string name or link
template <>
struct TC_GAME_API ArgInfo<GameTele const*>
{
static char const* TryConsume(GameTele const*&, char const*);
};
}}
#endif

View File

@@ -0,0 +1,75 @@
/*
* 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_CHATCOMMANDHELPERS_H
#define TRINITY_CHATCOMMANDHELPERS_H
#include "advstd.h"
#include <type_traits>
namespace Trinity {
namespace ChatCommands {
static const char COMMAND_DELIMITER = ' ';
/***************** HELPERS *************************\
|* These really aren't for outside use... *|
\***************************************************/
inline size_t tokenize(char const*& end)
{
size_t len = 0;
for (; *end && *end != COMMAND_DELIMITER; ++end, ++len);
for (; *end && *end == COMMAND_DELIMITER; ++end);
return len;
}
template <typename T, typename = void>
struct tag_base
{
using type = T;
};
template <typename T>
using tag_base_t = typename tag_base<T>::type;
template <typename...>
struct are_all_assignable
{
static constexpr bool value = true;
};
template <typename T1, typename T2, typename... Ts>
struct are_all_assignable<T1, T2, Ts...>
{
static constexpr bool value = advstd::is_assignable_v<T1&, T2> && are_all_assignable<T1, Ts...>::value;
};
template <size_t index, typename T1, typename... Ts>
struct get_nth : get_nth<index-1, Ts...> { };
template <typename T1, typename... Ts>
struct get_nth<0, T1, Ts...>
{
using type = T1;
};
template <size_t index, typename... Ts>
using get_nth_t = typename get_nth<index, Ts...>::type;
}}
#endif

View File

@@ -0,0 +1,132 @@
/*
* 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_CHATCOMMANDHYPERLINKS_H
#define TRINITY_CHATCOMMANDHYPERLINKS_H
#include "ChatCommandHelpers.h"
#include "ChatCommandTags.h"
#include "ObjectGuid.h"
namespace Trinity {
namespace ChatCommands {
// for details on what ContainerTag has to expose, see ChatCommandTags.h
template <typename linktag>
struct Hyperlink : public ContainerTag
{
typedef typename linktag::value_type value_type;
typedef advstd::remove_cvref_t<value_type> storage_type;
public:
operator value_type() const { return val; }
char const* TryConsume(char const* pos)
{
//color tag
if (*(pos++) != '|' || *(pos++) != 'c')
return nullptr;
for (uint8 i = 0; i < 8; ++i)
if (!*(pos++)) // make sure we don't overrun a terminator
return nullptr;
// link data start tag
if (*(pos++) != '|' || *(pos++) != 'H')
return nullptr;
// link tag, should match argument
char const* tag = linktag::tag();
while (*tag)
if (*(pos++) != *(tag++))
return nullptr;
// separator
if (*(pos++) != ':')
return nullptr;
// ok, link data, let's figure out how long it is
char const* datastart = pos;
size_t datalength = 0;
while (*pos && *(pos++) != '|')
++datalength;
// ok, next should be link data end tag...
if (*(pos++) != 'h')
return nullptr;
// then visible link text, skip to next '|', should be '|h|r' terminator
while (*pos && *(pos++) != '|');
if (*(pos++) != 'h' || *(pos++) != '|' || *(pos++) != 'r')
return nullptr;
// finally, skip to end of token
tokenize(pos);
// store value
if (!linktag::StoreTo(val, datastart, datalength))
return nullptr;
// return final pos
return pos;
}
private:
storage_type val;
};
/************************** LINK TAGS ***************************************************\
|* Link tags must abide by the following: *|
|* - MUST expose ::value_type typedef *|
|* - storage type is remove_cvref_t<value_type> *|
|* - MUST expose static ::tag method, void -> const char* *|
|* - this method SHOULD be constexpr *|
|* - returns identifier string for the link ("creature", "creature_entry", "item") *|
|* - MUST expose static ::StoreTo method, (storage&, char const*, size_t) *|
|* - assign value_type& based on content of std::string(char const*, size_t) *|
|* - return value indicates success/failure *|
|* - for integral/string types this can be achieved by extending base_tag *|
\****************************************************************************************/
struct base_tag
{
static bool StoreTo(std::string& val, char const* pos, size_t len)
{
val.assign(pos, len);
return true;
}
template <typename T>
static std::enable_if_t<advstd::is_integral_v<T> && advstd::is_unsigned_v<T>, bool> StoreTo(T& val, char const* pos, size_t len)
{
try { val = std::stoull(std::string(pos, len)); }
catch (...) { return false; }
return true;
}
template <typename T>
static std::enable_if_t<advstd::is_integral_v<T> && advstd::is_signed_v<T>, bool> StoreTo(T& val, char const* pos, size_t len)
{
try { val = std::stoll(std::string(pos, len)); }
catch (...) { return false; }
return true;
}
};
#define make_base_tag(ltag, type) struct ltag : public base_tag { typedef type value_type; static constexpr char const* tag() { return #ltag; } }
make_base_tag(areatrigger, uint32);
make_base_tag(creature, ObjectGuid::LowType);
make_base_tag(creature_entry, uint32);
make_base_tag(gameobject, ObjectGuid::LowType);
make_base_tag(taxinode, uint32);
make_base_tag(tele, uint32);
#undef make_base_tag
}}
#endif

View File

@@ -0,0 +1,119 @@
/*
* 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_CHATCOMMANDTAGS_H
#define TRINITY_CHATCOMMANDTAGS_H
#include "ChatCommandHelpers.h"
#include "Optional.h"
#include <boost/variant.hpp>
#include <cmath>
#include <cstring>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
namespace Trinity {
namespace ChatCommands {
/************************** CONTAINER TAGS **********************************************\
|* Simple holder classes to differentiate between extraction methods *|
|* Should inherit from ContainerTag for template identification *|
|* Must implement the following: *|
|* - TryConsume: char const* -> char const* *|
|* returns nullptr if no match, otherwise pointer to first character of next token *|
|* - typedef value_type of type that is contained within the tag *|
|* - cast operator to value_type *|
|* *|
\****************************************************************************************/
struct ContainerTag {};
template <typename T>
struct tag_base<T, std::enable_if_t<advstd::is_base_of_v<ContainerTag, T>>>
{
using type = typename T::value_type;
};
template <char c1, char... chars>
struct ExactSequence : public ContainerTag
{
using value_type = void;
template <size_t U = sizeof...(chars)>
static typename std::enable_if_t<U, char const*> TryConsume(char const* pos)
{
if (*(pos++) == c1)
return ExactSequence<chars...>::TryConsume(pos);
else
return nullptr;
}
template <size_t U = sizeof...(chars)>
static typename std::enable_if_t<!U, char const*> TryConsume(char const* pos)
{
if (*(pos++) != c1)
return nullptr;
// if more of string is left, tokenize should return 0 (otherwise we didn't reach end of token yet)
return *pos && tokenize(pos) ? nullptr : pos;
}
};
/************************** VARIANT TAG LOGIC *********************************\
|* This has some special handling over in ChatCommand.h *|
\******************************************************************************/
template <typename T>
struct CastToVisitor {
using result_type = T;
template <typename U>
T operator()(U const& v) const { return v; }
};
template <typename T1, typename... Ts>
struct Variant : public boost::variant<T1, Ts...>
{
using first_type = tag_base_t<T1>;
static constexpr bool have_operators = are_all_assignable<first_type, tag_base_t<Ts>...>::value;
template <bool C = have_operators>
operator std::enable_if_t<C, first_type>()
{
return operator*();
}
template <bool C = have_operators>
std::enable_if_t<C, first_type> operator*()
{
return boost::apply_visitor(CastToVisitor<first_type>(), *this);
}
template <bool C = have_operators>
std::enable_if_t<C, first_type const&> operator*() const
{
return boost::apply_visitor(CastToVisitor<first_type const&>(), *this);
}
template <typename T>
Variant& operator=(T&& arg) { boost::variant<T1, Ts...>::operator=(std::forward<T>(arg)); return *this; }
template <size_t index>
decltype(auto) get() const { return boost::get<get_nth_t<index, T1, Ts...>>(*this); }
};
}}
#endif

View File

@@ -32,8 +32,10 @@ EndScriptData */
#include "RBAC.h"
#include "TicketMgr.h"
#include "Transport.h"
#include "Util.h"
#include "WorldSession.h"
using namespace Trinity::ChatCommands;
class go_commandscript : public CommandScript
{
public:
@@ -41,14 +43,26 @@ public:
std::vector<ChatCommand> GetCommands() const override
{
static std::vector<ChatCommand> goCreatureCommandTable =
{
{ "id", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoCreatureCIdCommand, "" },
{ "", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoCreatureSpawnIdCommand, "" }
};
static std::vector<ChatCommand> goGameObjectCommandTable =
{
{ "id", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoGameObjectGOIdCommand, "" },
{ "", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoGameObjectSpawnIdCommand, "" }
};
static std::vector<ChatCommand> goCommandTable =
{
{ "creature", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoCreatureCommand, "" },
{ "creature", rbac::RBAC_PERM_COMMAND_GO, false, nullptr, "", goCreatureCommandTable },
{ "gameobject", rbac::RBAC_PERM_COMMAND_GO, false, nullptr, "", goGameObjectCommandTable },
{ "graveyard", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoGraveyardCommand, "" },
{ "grid", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoGridCommand, "" },
{ "object", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoObjectCommand, "" },
{ "taxinode", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoTaxinodeCommand, "" },
{ "trigger", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoTriggerCommand, "" },
{ "areatrigger", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoAreaTriggerCommand, "" },
{ "zonexy", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoZoneXYCommand, "" },
{ "xyz", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoXYZCommand, "" },
{ "ticket", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoTicketCommand, "" },
@@ -63,84 +77,13 @@ public:
return commandTable;
}
/** \brief Teleport the GM to the specified creature
*
* .gocreature <GUID> --> TP using creature.guid
* .gocreature azuregos --> TP player to the mob with this name
* Warning: If there is more than one mob with this name
* you will be teleported to the first one that is found.
* .gocreature id 6109 --> TP player to the mob, that has this creature_template.entry
* Warning: If there is more than one mob with this "id"
* you will be teleported to the first one that is found.
*/
//teleport to creature
static bool HandleGoCreatureCommand(ChatHandler* handler, char const* args)
static bool DoTeleport(ChatHandler* handler, WorldLocation loc)
{
if (!*args)
return false;
Player* player = handler->GetSession()->GetPlayer();
// "id" or number or [name] Shift-click form |color|Hcreature_entry:creature_id|h[name]|h|r
char* param1 = handler->extractKeyFromLink((char*)args, "Hcreature");
if (!param1)
return false;
std::ostringstream whereClause;
// User wants to teleport to the NPC's template entry
if (strcmp(param1, "id") == 0)
if (!MapManager::IsValidMapCoord(loc) || sObjectMgr->IsTransportMap(loc.GetMapId()))
{
// Get the "creature_template.entry"
// number or [name] Shift-click form |color|Hcreature_entry:creature_id|h[name]|h|r
char* tail = strtok(nullptr, "");
if (!tail)
return false;
char* id = handler->extractKeyFromLink(tail, "Hcreature_entry");
if (!id)
return false;
uint32 entry = atoul(id);
if (!entry)
return false;
whereClause << "WHERE id = '" << entry << '\'';
}
else
{
ObjectGuid::LowType guidLow = atoul(param1);
// Number is invalid - maybe the user specified the mob's name
if (!guidLow)
{
std::string name = param1;
WorldDatabase.EscapeString(name);
whereClause << ", creature_template WHERE creature.id = creature_template.entry AND creature_template.name LIKE '" << name << '\'';
}
else
whereClause << "WHERE guid = '" << guidLow << '\'';
}
QueryResult result = WorldDatabase.PQuery("SELECT position_x, position_y, position_z, orientation, map, guid, id FROM creature %s", whereClause.str().c_str());
if (!result)
{
handler->SendSysMessage(LANG_COMMAND_GOCREATNOTFOUND);
handler->SetSentErrorMessage(true);
return false;
}
if (result->GetRowCount() > 1)
handler->SendSysMessage(LANG_COMMAND_GOCREATMULTIPLE);
Field* fields = result->Fetch();
float x = fields[0].GetFloat();
float y = fields[1].GetFloat();
float z = fields[2].GetFloat();
float o = fields[3].GetFloat();
uint32 mapId = fields[4].GetUInt16();
if (!MapManager::IsValidMapCoord(mapId, x, y, z, o) || sObjectMgr->IsTransportMap(mapId))
{
handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, x, y, mapId);
handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ());
handler->SetSentErrorMessage(true);
return false;
}
@@ -151,30 +94,96 @@ public:
else
player->SaveRecallPosition(); // save only in non-flight case
player->TeleportTo(mapId, x, y, z, o);
player->TeleportTo(loc);
return true;
}
static bool HandleGoGraveyardCommand(ChatHandler* handler, char const* args)
static bool HandleGoCreatureSpawnIdCommand(ChatHandler* handler, Variant<Hyperlink<creature>, ObjectGuid::LowType> spawnId)
{
Player* player = handler->GetSession()->GetPlayer();
if (!*args)
CreatureData const* spawnpoint = sObjectMgr->GetCreatureData(spawnId);
if (!spawnpoint)
{
handler->SendSysMessage(LANG_COMMAND_GOCREATNOTFOUND);
handler->SetSentErrorMessage(true);
return false;
}
char* gyId = strtok((char*)args, " ");
if (!gyId)
return DoTeleport(handler, spawnpoint->spawnPoint);
}
static bool HandleGoCreatureCIdCommand(ChatHandler* handler, Variant<Hyperlink<creature_entry>, uint32> cId)
{
CreatureData const* spawnpoint = nullptr;
for (auto const& pair : sObjectMgr->GetAllCreatureData())
{
if (pair.second.id != *cId)
continue;
if (!spawnpoint)
spawnpoint = &pair.second;
else
{
handler->SendSysMessage(LANG_COMMAND_GOCREATMULTIPLE);
break;
}
}
if (!spawnpoint)
{
handler->SendSysMessage(LANG_COMMAND_GOCREATNOTFOUND);
handler->SetSentErrorMessage(true);
return false;
}
uint32 graveyardId = atoul(gyId);
return DoTeleport(handler, spawnpoint->spawnPoint);
}
if (!graveyardId)
static bool HandleGoGameObjectSpawnIdCommand(ChatHandler* handler, uint32 spawnId)
{
GameObjectData const* spawnpoint = sObjectMgr->GetGameObjectData(spawnId);
if (!spawnpoint)
{
handler->SendSysMessage(LANG_COMMAND_GOOBJNOTFOUND);
handler->SetSentErrorMessage(true);
return false;
}
WorldSafeLocsEntry const* gy = sWorldSafeLocsStore.LookupEntry(graveyardId);
return DoTeleport(handler, spawnpoint->spawnPoint);
}
static bool HandleGoGameObjectGOIdCommand(ChatHandler* handler, uint32 goId)
{
GameObjectData const* spawnpoint = nullptr;
for (auto const& pair : sObjectMgr->GetAllGameObjectData())
{
if (pair.second.id != goId)
continue;
if (!spawnpoint)
spawnpoint = &pair.second;
else
{
handler->SendSysMessage(LANG_COMMAND_GOCREATMULTIPLE);
break;
}
}
if (!spawnpoint)
{
handler->SendSysMessage(LANG_COMMAND_GOOBJNOTFOUND);
handler->SetSentErrorMessage(true);
return false;
}
return DoTeleport(handler, spawnpoint->spawnPoint);
}
static bool HandleGoGraveyardCommand(ChatHandler* handler, uint32 gyId)
{
WorldSafeLocsEntry const* gy = sWorldSafeLocsStore.LookupEntry(gyId);
if (!gy)
{
handler->PSendSysMessage(LANG_COMMAND_GRAVEYARDNOEXIST, graveyardId);
handler->PSendSysMessage(LANG_COMMAND_GRAVEYARDNOEXIST, gyId);
handler->SetSentErrorMessage(true);
return false;
}
@@ -186,6 +195,7 @@ public:
return false;
}
Player* player = handler->GetSession()->GetPlayer();
// stop flight if need
if (player->IsInFlight())
player->FinishTaxiFlight();
@@ -197,25 +207,14 @@ public:
}
//teleport to grid
static bool HandleGoGridCommand(ChatHandler* handler, char const* args)
static bool HandleGoGridCommand(ChatHandler* handler, float gridX, float gridY, Optional<uint32> oMapId)
{
if (!*args)
return false;
Player* player = handler->GetSession()->GetPlayer();
char* gridX = strtok((char*)args, " ");
char* gridY = strtok(nullptr, " ");
char* id = strtok(nullptr, " ");
if (!gridX || !gridY)
return false;
uint32 mapId = id ? atoul(id) : player->GetMapId();
uint32 mapId = oMapId.get_value_or(player->GetMapId());
// center of grid
float x = ((float)atof(gridX) - CENTER_GRID_ID + 0.5f) * SIZE_OF_GRIDS;
float y = ((float)atof(gridY) - CENTER_GRID_ID + 0.5f) * SIZE_OF_GRIDS;
float x = (gridX - CENTER_GRID_ID + 0.5f) * SIZE_OF_GRIDS;
float y = (gridY - CENTER_GRID_ID + 0.5f) * SIZE_OF_GRIDS;
if (!MapManager::IsValidMapCoord(mapId, x, y))
{
@@ -237,64 +236,8 @@ public:
return true;
}
//teleport to gameobject
static bool HandleGoObjectCommand(ChatHandler* handler, char const* args)
static bool HandleGoTaxinodeCommand(ChatHandler* handler, Variant<Hyperlink<taxinode>, uint32> nodeId)
{
if (!*args)
return false;
Player* player = handler->GetSession()->GetPlayer();
// number or [name] Shift-click form |color|Hgameobject:go_guid|h[name]|h|r
char* id = handler->extractKeyFromLink((char*)args, "Hgameobject");
if (!id)
return false;
ObjectGuid::LowType guidLow = atoul(id);
if (!guidLow)
return false;
// by DB guid
GameObjectData const* goData = sObjectMgr->GetGameObjectData(guidLow);
if (!goData)
{
handler->SendSysMessage(LANG_COMMAND_GOOBJNOTFOUND);
handler->SetSentErrorMessage(true);
return false;
}
if (!MapManager::IsValidMapCoord(goData->spawnPoint) || sObjectMgr->IsTransportMap(goData->spawnPoint.GetMapId()))
{
handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, goData->spawnPoint.GetPositionX(), goData->spawnPoint.GetPositionY(), goData->spawnPoint.GetMapId());
handler->SetSentErrorMessage(true);
return false;
}
// stop flight if need
if (player->IsInFlight())
player->FinishTaxiFlight();
else
player->SaveRecallPosition(); // save only in non-flight case
player->TeleportTo(goData->spawnPoint);
return true;
}
static bool HandleGoTaxinodeCommand(ChatHandler* handler, char const* args)
{
Player* player = handler->GetSession()->GetPlayer();
if (!*args)
return false;
char* id = handler->extractKeyFromLink((char*)args, "Htaxinode");
if (!id)
return false;
uint32 nodeId = atoul(id);
if (!nodeId)
return false;
TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(nodeId);
if (!node)
{
@@ -302,41 +245,11 @@ public:
handler->SetSentErrorMessage(true);
return false;
}
if ((node->x == 0.0f && node->y == 0.0f && node->z == 0.0f) ||
!MapManager::IsValidMapCoord(node->map_id, node->x, node->y, node->z))
{
handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, node->x, node->y, node->map_id);
handler->SetSentErrorMessage(true);
return false;
}
// stop flight if need
if (player->IsInFlight())
player->FinishTaxiFlight();
else
player->SaveRecallPosition(); // save only in non-flight case
player->TeleportTo(node->map_id, node->x, node->y, node->z, player->GetOrientation());
return true;
return DoTeleport(handler, { node->map_id, { node->x, node->y, node->z } });
}
static bool HandleGoTriggerCommand(ChatHandler* handler, char const* args)
static bool HandleGoAreaTriggerCommand(ChatHandler* handler, Variant<Hyperlink<areatrigger>, uint32> areaTriggerId)
{
Player* player = handler->GetSession()->GetPlayer();
if (!*args)
return false;
char* id = strtok((char*)args, " ");
if (!id)
return false;
uint32 areaTriggerId = atoul(id);
if (!areaTriggerId)
return false;
AreaTriggerEntry const* at = sAreaTriggerStore.LookupEntry(areaTriggerId);
if (!at)
{
@@ -344,22 +257,7 @@ public:
handler->SetSentErrorMessage(true);
return false;
}
if (!MapManager::IsValidMapCoord(at->mapid, at->x, at->y, at->z))
{
handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, at->x, at->y, at->mapid);
handler->SetSentErrorMessage(true);
return false;
}
// stop flight if need
if (player->IsInFlight())
player->FinishTaxiFlight();
else
player->SaveRecallPosition(); // save only in non-flight case
player->TeleportTo(at->mapid, at->x, at->y, at->z, player->GetOrientation());
return true;
return DoTeleport(handler, { at->mapid, { at->x, at->y, at->z } });
}
//teleport at coordinates
@@ -432,32 +330,13 @@ public:
}
//teleport at coordinates, including Z and orientation
static bool HandleGoXYZCommand(ChatHandler* handler, char const* args)
static bool HandleGoXYZCommand(ChatHandler* handler, float x, float y, Optional<float> z, Optional<uint32> id, Optional<float> o)
{
if (!*args)
return false;
Player* player = handler->GetSession()->GetPlayer();
char* goX = strtok((char*)args, " ");
char* goY = strtok(nullptr, " ");
char* goZ = strtok(nullptr, " ");
char* id = strtok(nullptr, " ");
char* port = strtok(nullptr, " ");
if (!goX || !goY)
return false;
float x = (float)atof(goX);
float y = (float)atof(goY);
float z;
float ort = port ? (float)atof(port) : player->GetOrientation();
uint32 mapId = id ? atoul(id) : player->GetMapId();
if (goZ)
uint32 mapId = id.get_value_or(player->GetMapId());
if (z)
{
z = (float)atof(goZ);
if (!MapManager::IsValidMapCoord(mapId, x, y, z))
if (!MapManager::IsValidMapCoord(mapId, x, y, *z))
{
handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, x, y, mapId);
handler->SetSentErrorMessage(true);
@@ -476,29 +355,11 @@ public:
z = std::max(map->GetHeight(x, y, MAX_HEIGHT), map->GetWaterLevel(x, y));
}
// stop flight if need
if (player->IsInFlight())
player->FinishTaxiFlight();
else
player->SaveRecallPosition(); // save only in non-flight case
player->TeleportTo(mapId, x, y, z, ort);
return true;
return DoTeleport(handler, { mapId, { x, y, *z, o.get_value_or(0.0f) } });
}
static bool HandleGoTicketCommand(ChatHandler* handler, char const* args)
static bool HandleGoTicketCommand(ChatHandler* handler, uint32 ticketId)
{
if (!*args)
return false;
char* id = strtok((char*)args, " ");
if (!id)
return false;
uint32 ticketId = atoul(id);
if (!ticketId)
return false;
GmTicket* ticket = sTicketMgr->GetTicket(ticketId);
if (!ticket)
{
@@ -518,95 +379,47 @@ public:
return true;
}
static bool HandleGoOffsetCommand(ChatHandler* handler, char const* args)
static bool HandleGoOffsetCommand(ChatHandler* handler, float dX, Optional<float> dY, Optional<float> dZ, Optional<float> dO)
{
if (!*args)
return false;
WorldLocation loc = handler->GetSession()->GetPlayer()->GetWorldLocation();
loc.RelocateOffset({ dX, dY.get_value_or(0.0f), dZ.get_value_or(0.0f), dO.get_value_or(0.0f) });
Player* player = handler->GetSession()->GetPlayer();
char* goX = strtok((char*)args, " ");
char* goY = strtok(nullptr, " ");
char* goZ = strtok(nullptr, " ");
char* port = strtok(nullptr, " ");
float x, y, z, o;
player->GetPosition(x, y, z, o);
if (goX)
x += atof(goX);
if (goY)
y += atof(goY);
if (goZ)
z += atof(goZ);
if (port)
o += atof(port);
if (!Trinity::IsValidMapCoord(x, y, z, o))
{
handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, x, y, player->GetMapId());
handler->SetSentErrorMessage(true);
return false;
}
// stop flight if need
if (player->IsInFlight())
player->FinishTaxiFlight();
else
player->SaveRecallPosition(); // save only in non-flight case
player->TeleportTo(player->GetMapId(), x, y, z, o);
return true;
return DoTeleport(handler, loc);
}
static bool HandleGoInstanceCommand(ChatHandler* handler, char const* args)
static bool HandleGoInstanceCommand(ChatHandler* handler, std::vector<std::string> const& labels)
{
if (!*args)
return false;
char* pos = const_cast<char*>(args);
do *pos = tolower(*pos);
while (*(++pos));
Tokenizer labels(args, ' ');
uint32 mapid = 0;
if (labels.size() == 1)
std::multimap<uint32, std::pair<uint16, std::string>> matches;
for (auto const& pair : sObjectMgr->GetInstanceTemplates())
{
try { mapid = std::stoi(labels[0]); }
catch (...) {}
uint32 count = 0;
std::string const& scriptName = sObjectMgr->GetScriptName(pair.second.ScriptId);
for (auto const& label : labels)
if (StringContainsStringI(scriptName, label))
++count;
if (count)
matches.emplace(count, decltype(matches)::mapped_type({ pair.first, scriptName }));
}
if (!mapid)
if (matches.empty())
{
std::multimap<uint32, std::pair<uint16, std::string>> matches;
for (auto const& pair : sObjectMgr->GetInstanceTemplates())
{
uint32 count = 0;
std::string const& scriptName = sObjectMgr->GetScriptName(pair.second.ScriptId);
for (char const* label : labels)
if (scriptName.find(label) != std::string::npos)
++count;
if (count)
matches.emplace(count, decltype(matches)::mapped_type({ pair.first, scriptName }));
}
if (matches.empty())
{
handler->SendSysMessage(LANG_COMMAND_NO_INSTANCES_MATCH);
return false;
}
auto it = matches.rbegin();
uint32 maxCount = it->first;
mapid = it->second.first;
if (++it != matches.rend() && it->first == maxCount)
{
handler->SendSysMessage(LANG_COMMAND_MULTIPLE_INSTANCES_MATCH);
--it;
do
handler->PSendSysMessage(LANG_COMMAND_MULTIPLE_INSTANCES_ENTRY, it->second.first, it->second.second);
while (++it != matches.rend() && it->first == maxCount);
handler->SetSentErrorMessage(true);
return false;
}
handler->SendSysMessage(LANG_COMMAND_NO_INSTANCES_MATCH);
return false;
}
auto it = matches.rbegin();
uint32 maxCount = it->first;
mapid = it->second.first;
if (++it != matches.rend() && it->first == maxCount)
{
handler->SendSysMessage(LANG_COMMAND_MULTIPLE_INSTANCES_MATCH);
--it;
do
handler->PSendSysMessage(LANG_COMMAND_MULTIPLE_INSTANCES_ENTRY, it->second.first, it->second.second);
while (++it != matches.rend() && it->first == maxCount);
handler->SetSentErrorMessage(true);
return false;
}
ASSERT(mapid);

View File

@@ -34,6 +34,8 @@ EndScriptData */
#include "RBAC.h"
#include "WorldSession.h"
using namespace Trinity::ChatCommands;
class tele_commandscript : public CommandScript
{
public:
@@ -56,17 +58,12 @@ public:
return commandTable;
}
static bool HandleTeleAddCommand(ChatHandler* handler, char const* args)
static bool HandleTeleAddCommand(ChatHandler* handler, std::string const& name)
{
if (!*args)
return false;
Player* player = handler->GetSession()->GetPlayer();
if (!player)
return false;
std::string name = args;
if (sObjectMgr->GetGameTeleExactName(name))
{
handler->SendSysMessage(LANG_COMMAND_TP_ALREADYEXIST);
@@ -96,13 +93,8 @@ public:
return true;
}
static bool HandleTeleDelCommand(ChatHandler* handler, char const* args)
static bool HandleTeleDelCommand(ChatHandler* handler, GameTele const* tele)
{
if (!*args)
return false;
// id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r
GameTele const* tele = handler->extractGameTeleFromLink((char*)args);
if (!tele)
{
handler->SendSysMessage(LANG_COMMAND_TELE_NOTFOUND);
@@ -209,10 +201,14 @@ public:
}
//Teleport group to given game_tele.entry
static bool HandleTeleGroupCommand(ChatHandler* handler, char const* args)
static bool HandleTeleGroupCommand(ChatHandler* handler, GameTele const* tele)
{
if (!*args)
if (!tele)
{
handler->SendSysMessage(LANG_COMMAND_TELE_NOTFOUND);
handler->SetSentErrorMessage(true);
return false;
}
Player* target = handler->getSelectedPlayer();
if (!target)
@@ -226,15 +222,6 @@ public:
if (handler->HasLowerSecurity(target, ObjectGuid::Empty))
return false;
// id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r
GameTele const* tele = handler->extractGameTeleFromLink((char*)args);
if (!tele)
{
handler->SendSysMessage(LANG_COMMAND_TELE_NOTFOUND);
handler->SetSentErrorMessage(true);
return false;
}
MapEntry const* map = sMapStore.LookupEntry(tele->mapId);
if (!map || map->IsBattlegroundOrArena())
{
@@ -288,15 +275,8 @@ public:
return true;
}
static bool HandleTeleCommand(ChatHandler* handler, char const* args)
static bool HandleTeleCommand(ChatHandler* handler, GameTele const* tele)
{
if (!*args)
return false;
Player* player = handler->GetSession()->GetPlayer();
// id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r
GameTele const* tele = handler->extractGameTeleFromLink((char*)args);
if (!tele)
{
handler->SendSysMessage(LANG_COMMAND_TELE_NOTFOUND);
@@ -304,6 +284,7 @@ public:
return false;
}
Player* player = handler->GetSession()->GetPlayer();
if (player->IsInCombat() && !handler->GetSession()->HasPermission(rbac::RBAC_PERM_COMMAND_TELE_NAME))
{
handler->SendSysMessage(LANG_YOU_IN_COMBAT);