diff options
Diffstat (limited to 'src/script/api/script_object.hpp')
-rw-r--r-- | src/script/api/script_object.hpp | 81 |
1 files changed, 78 insertions, 3 deletions
diff --git a/src/script/api/script_object.hpp b/src/script/api/script_object.hpp index 40c8eaf85..cfb6d393a 100644 --- a/src/script/api/script_object.hpp +++ b/src/script/api/script_object.hpp @@ -13,7 +13,8 @@ #include "../../misc/countedptr.hpp" #include "../../road_type.h" #include "../../rail_type.h" -#include "../../command_type.h" +#include "../../string_func.h" +#include "../../command_func.h" #include "script_types.hpp" #include "../script_suspend.hpp" @@ -67,10 +68,32 @@ public: static class ScriptInstance *GetActiveInstance(); protected: + template<Commands TCmd, typename T> struct ScriptDoCommandHelper; + /** - * Executes a raw DoCommand for the script. + * Templated wrapper that exposes the command parameter arguments + * on the various DoCommand calls. + * @tparam Tcmd The command-id to execute. + * @tparam Targs The command parameter types. */ - static bool DoCommand(TileIndex tile, uint32 p1, uint32 p2, Commands cmd, const char *text = nullptr, Script_SuspendCallbackProc *callback = nullptr); + template <Commands Tcmd, typename... Targs> + struct ScriptDoCommandHelper<Tcmd, CommandCost(*)(DoCommandFlag, Targs...)> { + static bool Do(Script_SuspendCallbackProc *callback, Targs... args) + { + return Execute(callback, std::forward_as_tuple(args...)); + } + + static bool Do(Targs... args) + { + return Execute(nullptr, std::forward_as_tuple(args...)); + } + + private: + static bool Execute(Script_SuspendCallbackProc *callback, std::tuple<Targs...> args); + }; + + template <Commands Tcmd> + using Command = ScriptDoCommandHelper<Tcmd, typename ::CommandTraits<Tcmd>::ProcType>; /** * Store the latest command executed by the script. @@ -299,6 +322,58 @@ private: * @param story_page_id The new StoryPageID. */ static void SetNewStoryPageElementID(StoryPageElementID story_page_element_id); + + /* Helper functions for DoCommand. */ + static std::tuple<bool, bool, bool> DoCommandPrep(); + static bool DoCommandProcessResult(const CommandCost &res, Script_SuspendCallbackProc *callback, bool estimate_only); + static CommandCallback *GetDoCommandCallback(); }; +namespace ScriptObjectInternal { + /** Validate a single string argument coming from network. */ + template <class T> + static inline void SanitizeSingleStringHelper(T &data) + { + if constexpr (std::is_same_v<std::string, T>) { + /* The string must be valid, i.e. not contain special codes. Since some + * can be made with GSText, make sure the control codes are removed. */ + data = ::StrMakeValid(data, SVS_NONE); + } + } + + /** Helper function to perform validation on command data strings. */ + template<class Ttuple, size_t... Tindices> + static inline void SanitizeStringsHelper(Ttuple &values, std::index_sequence<Tindices...>) + { + ((SanitizeSingleStringHelper(std::get<Tindices>(values))), ...); + } +} + +template <Commands Tcmd, typename... Targs> +bool ScriptObject::ScriptDoCommandHelper<Tcmd, CommandCost(*)(DoCommandFlag, Targs...)>::Execute(Script_SuspendCallbackProc *callback, std::tuple<Targs...> args) +{ + auto [err, estimate_only, networking] = ScriptObject::DoCommandPrep(); + if (err) return false; + + if ((::GetCommandFlags<Tcmd>() & CMD_STR_CTRL) == 0) { + ScriptObjectInternal::SanitizeStringsHelper(args, std::index_sequence_for<Targs...>{}); + } + + TileIndex tile{}; + if constexpr (std::is_same_v<TileIndex, std::tuple_element_t<0, decltype(args)>>) { + tile = std::get<0>(args); + } + + /* Only set p2 when the command does not come from the network. */ + if ((::GetCommandFlags<Tcmd>() & CMD_CLIENT_ID) != 0 && std::get<2>(args) == 0) std::get<2>(args) = UINT32_MAX; + + /* Store the command for command callback validation. */ + if (!estimate_only && networking) ScriptObject::SetLastCommand(tile, EndianBufferWriter<CommandDataBuffer>::FromValue(args), Tcmd); + + /* Try to perform the command. */ + CommandCost res = std::apply(&DoCommandPInternal, std::tuple_cat(std::make_tuple(Tcmd, (StringID)0, networking ? ScriptObject::GetDoCommandCallback() : nullptr, false, estimate_only, false), args)); + + return ScriptObject::DoCommandProcessResult(res, callback, estimate_only); +} + #endif /* SCRIPT_OBJECT_HPP */ |