aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTreeston <treeston.mmoc@gmail.com>2018-09-07 20:31:04 +0200
committerShauren <shauren.trinity@gmail.com>2021-10-25 00:03:23 +0200
commit0c681b6509d4b517dc2e65a152753224df745605 (patch)
tree61c77765713f13d5d7006ef90e6c0a392ee6328a /src
parent42f366648f6b65753b3719f66b406e110ec9a871 (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.cpp8
-rw-r--r--src/common/Utilities/Util.h2
-rw-r--r--src/common/Utilities/advstd.h1
-rw-r--r--src/server/game/Chat/Chat.cpp9
-rw-r--r--src/server/game/Chat/Chat.h16
-rw-r--r--src/server/game/Chat/ChatCommands/ChatCommand.h279
-rw-r--r--src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp37
-rw-r--r--src/server/game/Chat/ChatCommands/ChatCommandArgs.h120
-rw-r--r--src/server/game/Chat/ChatCommands/ChatCommandHelpers.h75
-rw-r--r--src/server/game/Chat/ChatCommands/ChatCommandHyperlinks.h132
-rw-r--r--src/server/game/Chat/ChatCommands/ChatCommandTags.h123
-rw-r--r--src/server/scripts/Commands/cs_go.cpp455
-rw-r--r--src/server/scripts/Commands/cs_tele.cpp43
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);