summaryrefslogtreecommitdiff
path: root/src/script/api/script_object.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/script/api/script_object.hpp')
-rw-r--r--src/script/api/script_object.hpp81
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 */