diff options
author | Treeston <treeston.mmoc@gmail.com> | 2018-09-07 20:31:04 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2021-10-25 00:03:23 +0200 |
commit | 0c681b6509d4b517dc2e65a152753224df745605 (patch) | |
tree | 61c77765713f13d5d7006ef90e6c0a392ee6328a /src | |
parent | 42f366648f6b65753b3719f66b406e110ec9a871 (diff) |
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*
(cherry picked from commit 66a87c4642d25f27ca24254cfeb0a0c4b21036b1)
Diffstat (limited to 'src')
-rw-r--r-- | src/common/Utilities/Util.cpp | 8 | ||||
-rw-r--r-- | src/common/Utilities/Util.h | 2 | ||||
-rw-r--r-- | src/common/Utilities/advstd.h | 1 | ||||
-rw-r--r-- | src/server/game/Chat/Chat.cpp | 9 | ||||
-rw-r--r-- | src/server/game/Chat/Chat.h | 16 | ||||
-rw-r--r-- | src/server/game/Chat/ChatCommands/ChatCommand.h | 279 | ||||
-rw-r--r-- | src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp | 37 | ||||
-rw-r--r-- | src/server/game/Chat/ChatCommands/ChatCommandArgs.h | 120 | ||||
-rw-r--r-- | src/server/game/Chat/ChatCommands/ChatCommandHelpers.h | 75 | ||||
-rw-r--r-- | src/server/game/Chat/ChatCommands/ChatCommandHyperlinks.h | 132 | ||||
-rw-r--r-- | src/server/game/Chat/ChatCommands/ChatCommandTags.h | 123 | ||||
-rw-r--r-- | src/server/scripts/Commands/cs_go.cpp | 455 | ||||
-rw-r--r-- | src/server/scripts/Commands/cs_tele.cpp | 43 |
13 files changed, 926 insertions, 374 deletions
diff --git a/src/common/Utilities/Util.cpp b/src/common/Utilities/Util.cpp index d5c1137cdd7..b8550c9a72e 100644 --- a/src/common/Utilities/Util.cpp +++ b/src/common/Utilities/Util.cpp @@ -22,6 +22,8 @@ #include <utf8.h> #include <algorithm> #include <sstream> +#include <string> +#include <cctype> #include <cstdarg> #include <ctime> @@ -755,6 +757,12 @@ bool StringToBool(std::string const& str) 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); }); +} + float DegToRad(float degrees) { return degrees * (2.f * float(M_PI) / 360.f); diff --git a/src/common/Utilities/Util.h b/src/common/Utilities/Util.h index d7c26fb98da..259af5068ff 100644 --- a/src/common/Utilities/Util.h +++ b/src/common/Utilities/Util.h @@ -352,6 +352,8 @@ std::array<uint8, Size> HexStrToByteArray(std::string const& str, bool reverse = TC_COMMON_API bool StringToBool(std::string const& str); TC_COMMON_API float DegToRad(float degrees); +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 diff --git a/src/common/Utilities/advstd.h b/src/common/Utilities/advstd.h index f2db86f11b2..975fc2c85ad 100644 --- a/src/common/Utilities/advstd.h +++ b/src/common/Utilities/advstd.h @@ -21,6 +21,7 @@ #include <cstddef> #include <initializer_list> #include <type_traits> +#include <utility> // this namespace holds implementations of upcoming stdlib features that our c++ version doesn't have yet namespace advstd diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp index 4cc184ae55f..2d180a6b1b9 100644 --- a/src/server/game/Chat/Chat.cpp +++ b/src/server/game/Chat/Chat.cpp @@ -38,11 +38,6 @@ #include "WorldSession.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 @@ -309,12 +304,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; diff --git a/src/server/game/Chat/Chat.h b/src/server/game/Chat/Chat.h index c9624ed0703..c450d5ebd27 100644 --- a/src/server/game/Chat/Chat.h +++ b/src/server/game/Chat/Chat.h @@ -18,6 +18,7 @@ #ifndef TRINITYCORE_CHAT_H #define TRINITYCORE_CHAT_H +#include "ChatCommand.h" #include "ObjectGuid.h" #include "SharedDefines.h" #include "StringFormat.h" @@ -36,21 +37,6 @@ struct GameTele; enum LocaleConstant : uint8; -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: diff --git a/src/server/game/Chat/ChatCommands/ChatCommand.h b/src/server/game/Chat/ChatCommands/ChatCommand.h new file mode 100644 index 00000000000..b15e57582ff --- /dev/null +++ b/src/server/game/Chat/ChatCommands/ChatCommand.h @@ -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 T2, typename... Ts> + 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 TryConsumeTo<V, T2, 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 diff --git a/src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp b/src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp new file mode 100644 index 00000000000..1efbc00dd46 --- /dev/null +++ b/src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp @@ -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; +} diff --git a/src/server/game/Chat/ChatCommands/ChatCommandArgs.h b/src/server/game/Chat/ChatCommands/ChatCommandArgs.h new file mode 100644 index 00000000000..10aa141a4c3 --- /dev/null +++ b/src/server/game/Chat/ChatCommands/ChatCommandArgs.h @@ -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 diff --git a/src/server/game/Chat/ChatCommands/ChatCommandHelpers.h b/src/server/game/Chat/ChatCommands/ChatCommandHelpers.h new file mode 100644 index 00000000000..f099d7eac57 --- /dev/null +++ b/src/server/game/Chat/ChatCommands/ChatCommandHelpers.h @@ -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 diff --git a/src/server/game/Chat/ChatCommands/ChatCommandHyperlinks.h b/src/server/game/Chat/ChatCommands/ChatCommandHyperlinks.h new file mode 100644 index 00000000000..f3fcf74ecf4 --- /dev/null +++ b/src/server/game/Chat/ChatCommands/ChatCommandHyperlinks.h @@ -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 + diff --git a/src/server/game/Chat/ChatCommands/ChatCommandTags.h b/src/server/game/Chat/ChatCommands/ChatCommandTags.h new file mode 100644 index 00000000000..c28c4f21a8f --- /dev/null +++ b/src/server/game/Chat/ChatCommands/ChatCommandTags.h @@ -0,0 +1,123 @@ +/* + * 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; + + static constexpr bool isSingleChar = !sizeof...(chars); + + template <bool C = isSingleChar> + static typename std::enable_if_t<!C, char const*> _TryConsume(char const* pos) + { + if (*(pos++) == c1) + return ExactSequence<chars...>::_TryConsume(pos); + else + return nullptr; + } + + template <bool C = isSingleChar> + static typename std::enable_if_t<C, 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; + } + + char const* TryConsume(char const* pos) const { return ExactSequence::_TryConsume(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 diff --git a/src/server/scripts/Commands/cs_go.cpp b/src/server/scripts/Commands/cs_go.cpp index 75c8822e171..ffd3181d205 100644 --- a/src/server/scripts/Commands/cs_go.cpp +++ b/src/server/scripts/Commands/cs_go.cpp @@ -34,9 +34,11 @@ EndScriptData */ #include "RBAC.h" #include "SupportMgr.h" #include "Transport.h" +#include "Util.h" #include "WorldSession.h" #include <sstream> +using namespace Trinity::ChatCommands; class go_commandscript : public CommandScript { public: @@ -44,15 +46,27 @@ 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, "" }, { "quest", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoQuestCommand, "" }, { "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, "" }, { "bugticket", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoTicketCommand<BugTicket>, "" }, @@ -69,118 +83,113 @@ 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) + if (!MapManager::IsValidMapCoord(loc) || sObjectMgr->IsTransportMap(loc.GetMapId())) + { + handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ()); + handler->SetSentErrorMessage(true); return false; + } + + // stop flight if need + if (player->IsInFlight()) + player->FinishTaxiFlight(); + else + player->SaveRecallPosition(); // save only in non-flight case - std::ostringstream whereClause; + player->TeleportTo(loc); + return true; + } - // User wants to teleport to the NPC's template entry - if (strcmp(param1, "id") == 0) + static bool HandleGoCreatureSpawnIdCommand(ChatHandler* handler, Variant<Hyperlink<creature>, ObjectGuid::LowType> spawnId) + { + CreatureData const* spawnpoint = sObjectMgr->GetCreatureData(spawnId); + if (!spawnpoint) { - // 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; + handler->SendSysMessage(LANG_COMMAND_GOCREATNOTFOUND); + handler->SetSentErrorMessage(true); + return false; + } - uint32 entry = atoul(id); - if (!entry) - return false; + return DoTeleport(handler, spawnpoint->spawnPoint); + } - whereClause << "WHERE id = '" << entry << '\''; - } - else + static bool HandleGoCreatureCIdCommand(ChatHandler* handler, Variant<Hyperlink<creature_entry>, uint32> cId) + { + CreatureData const* spawnpoint = nullptr; + for (auto const& pair : sObjectMgr->GetAllCreatureData()) { - ObjectGuid::LowType guidLow = atoull(param1); + if (pair.second.id != *cId) + continue; - // Number is invalid - maybe the user specified the mob's name - if (!guidLow) + if (!spawnpoint) + spawnpoint = &pair.second; + else { - std::string name = param1; - WorldDatabase.EscapeString(name); - whereClause << ", creature_template WHERE creature.id = creature_template.entry AND creature_template.name LIKE '" << name << '\''; + handler->SendSysMessage(LANG_COMMAND_GOCREATMULTIPLE); + break; } - else - whereClause << "WHERE guid = '" << guidLow << '\''; } - QueryResult result = WorldDatabase.PQuery("SELECT position_x, position_y, position_z, orientation, map FROM creature %s", whereClause.str().c_str()); - if (!result) + if (!spawnpoint) { 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(); + return DoTeleport(handler, spawnpoint->spawnPoint); + } - if (!MapManager::IsValidMapCoord(mapId, x, y, z, o) || sObjectMgr->IsTransportMap(mapId)) + static bool HandleGoGameObjectSpawnIdCommand(ChatHandler* handler, uint32 spawnId) + { + GameObjectData const* spawnpoint = sObjectMgr->GetGameObjectData(spawnId); + if (!spawnpoint) { - handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, x, y, mapId); + handler->SendSysMessage(LANG_COMMAND_GOOBJNOTFOUND); 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(mapId, x, y, z, o); - return true; + return DoTeleport(handler, spawnpoint->spawnPoint); } - static bool HandleGoGraveyardCommand(ChatHandler* handler, char const* args) + static bool HandleGoGameObjectGOIdCommand(ChatHandler* handler, uint32 goId) { - Player* player = handler->GetSession()->GetPlayer(); + GameObjectData const* spawnpoint = nullptr; + for (auto const& pair : sObjectMgr->GetAllGameObjectData()) + { + if (pair.second.id != goId) + continue; - if (!*args) - return false; + if (!spawnpoint) + spawnpoint = &pair.second; + else + { + handler->SendSysMessage(LANG_COMMAND_GOCREATMULTIPLE); + break; + } + } - char* gyId = strtok((char*)args, " "); - if (!gyId) + if (!spawnpoint) + { + handler->SendSysMessage(LANG_COMMAND_GOOBJNOTFOUND); + handler->SetSentErrorMessage(true); return false; + } - uint32 graveyardId = atoul(gyId); - - if (!graveyardId) - return false; + return DoTeleport(handler, spawnpoint->spawnPoint); + } - WorldSafeLocsEntry const* gy = sObjectMgr->GetWorldSafeLoc(graveyardId); + static bool HandleGoGraveyardCommand(ChatHandler* handler, uint32 gyId) + { + WorldSafeLocsEntry const* gy = sObjectMgr->GetWorldSafeLoc(gyId); if (!gy) { - handler->PSendSysMessage(LANG_COMMAND_GRAVEYARDNOEXIST, graveyardId); + handler->PSendSysMessage(LANG_COMMAND_GRAVEYARDNOEXIST, gyId); handler->SetSentErrorMessage(true); return false; } @@ -192,6 +201,7 @@ public: return false; } + Player* player = handler->GetSession()->GetPlayer(); // stop flight if need if (player->IsInFlight()) player->FinishTaxiFlight(); @@ -203,25 +213,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)) { @@ -243,49 +242,6 @@ public: return true; } - //teleport to gameobject - static bool HandleGoObjectCommand(ChatHandler* handler, char const* args) - { - 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 = atoull(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 HandleGoQuestCommand(ChatHandler* handler, char const* args) { if (!*args) @@ -348,21 +304,8 @@ public: return true; } - static bool HandleGoTaxinodeCommand(ChatHandler* handler, char const* args) + static bool HandleGoTaxinodeCommand(ChatHandler* handler, Variant<Hyperlink<taxinode>, uint32> nodeId) { - 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) { @@ -370,41 +313,11 @@ public: handler->SetSentErrorMessage(true); return false; } - - if ((node->Pos.X == 0.0f && node->Pos.Y == 0.0f && node->Pos.Z == 0.0f) || - !MapManager::IsValidMapCoord(node->ContinentID, node->Pos.X, node->Pos.Y, node->Pos.Z)) - { - handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, node->Pos.X, node->Pos.Y, uint32(node->ContinentID)); - 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->ContinentID, node->Pos.X, node->Pos.Y, node->Pos.Z, player->GetOrientation()); - return true; + return DoTeleport(handler, { node->ContinentID, { node->Pos.X, node->Pos.Y, node->Pos.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) { @@ -412,22 +325,7 @@ public: handler->SetSentErrorMessage(true); return false; } - - if (!MapManager::IsValidMapCoord(at->ContinentID, at->Pos.X, at->Pos.Y, at->Pos.Z)) - { - handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, at->Pos.X, at->Pos.Y, uint32(at->ContinentID)); - 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->ContinentID, at->Pos.X, at->Pos.Y, at->Pos.Z, player->GetOrientation()); - return true; + return DoTeleport(handler, { uint32(at->ContinentID), { at->Pos.X, at->Pos.Y, at->Pos.Z } }); } //teleport at coordinates @@ -503,32 +401,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); @@ -547,30 +426,12 @@ public: z = std::max(map->GetStaticHeight(PhasingHandler::GetEmptyPhaseShift(), x, y, MAX_HEIGHT), map->GetWaterLevel(PhasingHandler::GetEmptyPhaseShift(), 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) } }); } template<typename T> - 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; - T* ticket = sSupportMgr->GetTicket<T>(ticketId); if (!ticket) { @@ -590,95 +451,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; - - Player* player = handler->GetSession()->GetPlayer(); + 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) }); - 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<uint32, 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<uint32, 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.c_str()); - 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); diff --git a/src/server/scripts/Commands/cs_tele.cpp b/src/server/scripts/Commands/cs_tele.cpp index 75a5aa88d68..16e08b52ed3 100644 --- a/src/server/scripts/Commands/cs_tele.cpp +++ b/src/server/scripts/Commands/cs_tele.cpp @@ -35,6 +35,8 @@ EndScriptData */ #include "RBAC.h" #include "WorldSession.h" +using namespace Trinity::ChatCommands; + class tele_commandscript : public CommandScript { public: @@ -57,17 +59,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); @@ -97,13 +94,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); @@ -210,10 +202,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) @@ -227,15 +223,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()) { @@ -289,15 +276,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); @@ -305,6 +285,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); |