2 * This file is part of OpenTTD.
3 * OpenTTD 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, version 2.
4 * OpenTTD 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.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 /** @file script_object.hpp Main object, on which all objects depend. */
10 #ifndef SCRIPT_OBJECT_HPP
11 #define SCRIPT_OBJECT_HPP
13 #include "../../misc/countedptr.hpp"
14 #include "../../road_type.h"
15 #include "../../rail_type.h"
16 #include "../../string_func.h"
17 #include "../../command_func.h"
18 #include "../../core/random_func.hpp"
20 #include "script_types.hpp"
21 #include "script_log_types.hpp"
22 #include "../script_suspend.hpp"
23 #include "../squirrel.hpp"
28 * The callback function for Mode-classes.
30 typedef bool (ScriptModeProc
)();
33 * The callback function for Async Mode-classes.
35 typedef bool (ScriptAsyncModeProc
)();
38 * Uper-parent object of all API classes. You should never use this class in
39 * your script, as it doesn't publish any public functions. It is used
40 * internally to have a common place to handle general things, like internal
41 * command processing, and command-validation checks.
44 class ScriptObject
: public SimpleCountedObject
{
45 friend class ScriptInstance
;
46 friend class ScriptController
;
47 friend class TestScriptController
;
50 * A class that handles the current active instance. By instantiating it at
51 * the beginning of a function with the current active instance, it remains
52 * active till the scope of the variable closes. It then automatically
53 * reverts to the active instance it was before instantiating.
55 class ActiveInstance
{
56 friend class ScriptObject
;
58 ActiveInstance(ScriptInstance
*instance
);
61 ScriptInstance
*last_active
; ///< The active instance before we go instantiated.
62 ScriptAllocatorScope alc_scope
; ///< Keep the correct allocator for the script instance activated
64 static ScriptInstance
*active
; ///< The global current active instance.
69 * Store the latest result of a DoCommand per company.
70 * @param res The result of the last command.
72 static void SetLastCommandRes(bool res
);
75 * Store the extra data return by the last DoCommand.
76 * @param data Extra data return by the command.
78 static void SetLastCommandResData(CommandDataBuffer data
);
81 * Get the currently active instance.
82 * @return The instance.
84 static class ScriptInstance
*GetActiveInstance();
87 * Get a reference of the randomizer that brings this script random values.
88 * @param owner The owner/script to get the randomizer for. This defaults to ScriptObject::GetRootCompany()
90 static Randomizer
&GetRandomizer(Owner owner
= ScriptObject::GetRootCompany());
93 * Initialize/reset the script random states. The state of the scripts are
94 * based on the current _random seed, but _random does not get changed.
96 static void InitializeRandomizers();
99 template<Commands TCmd
, typename T
> struct ScriptDoCommandHelper
;
102 * Templated wrapper that exposes the command parameter arguments
103 * on the various DoCommand calls.
104 * @tparam Tcmd The command-id to execute.
105 * @tparam Tret Return type of the command.
106 * @tparam Targs The command parameter types.
108 template <Commands Tcmd
, typename Tret
, typename
... Targs
>
109 struct ScriptDoCommandHelper
<Tcmd
, Tret(*)(DoCommandFlag
, Targs
...)> {
110 static bool Do(Script_SuspendCallbackProc
*callback
, Targs
... args
)
112 return Execute(callback
, std::forward_as_tuple(args
...));
115 static bool Do(Targs
... args
)
117 return Execute(nullptr, std::forward_as_tuple(args
...));
121 static bool Execute(Script_SuspendCallbackProc
*callback
, std::tuple
<Targs
...> args
);
124 template <Commands Tcmd
>
125 using Command
= ScriptDoCommandHelper
<Tcmd
, typename ::CommandTraits
<Tcmd
>::ProcType
>;
128 * Store the latest command executed by the script.
130 static void SetLastCommand(const CommandDataBuffer
&data
, Commands cmd
);
133 * Check if it's the latest command executed by the script.
135 static bool CheckLastCommand(const CommandDataBuffer
&data
, Commands cmd
);
138 * Sets the DoCommand costs counter to a value.
140 static void SetDoCommandCosts(Money value
);
143 * Increase the current value of the DoCommand costs counter.
145 static void IncreaseDoCommandCosts(Money value
);
148 * Get the current DoCommand costs counter.
150 static Money
GetDoCommandCosts();
153 * Set the DoCommand last error.
155 static void SetLastError(ScriptErrorType last_error
);
158 * Get the DoCommand last error.
160 static ScriptErrorType
GetLastError();
165 static void SetRoadType(RoadType road_type
);
170 static RoadType
GetRoadType();
175 static void SetRailType(RailType rail_type
);
180 static RailType
GetRailType();
183 * Set the current mode of your script to this proc.
185 static void SetDoCommandMode(ScriptModeProc
*proc
, ScriptObject
*instance
);
188 * Get the current mode your script is currently under.
190 static ScriptModeProc
*GetDoCommandMode();
193 * Get the instance of the current mode your script is currently under.
195 static ScriptObject
*GetDoCommandModeInstance();
198 * Set the current async mode of your script to this proc.
200 static void SetDoCommandAsyncMode(ScriptAsyncModeProc
*proc
, ScriptObject
*instance
);
203 * Get the current async mode your script is currently under.
205 static ScriptModeProc
*GetDoCommandAsyncMode();
208 * Get the instance of the current async mode your script is currently under.
210 static ScriptObject
*GetDoCommandAsyncModeInstance();
213 * Set the delay of the DoCommand.
215 static void SetDoCommandDelay(uint ticks
);
218 * Get the delay of the DoCommand.
220 static uint
GetDoCommandDelay();
223 * Get the latest result of a DoCommand.
225 static bool GetLastCommandRes();
228 * Get the extra return data from the last DoCommand.
230 static const CommandDataBuffer
&GetLastCommandResData();
233 * Store a allow_do_command per company.
234 * @param allow The new allow.
236 static void SetAllowDoCommand(bool allow
);
239 * Get the internal value of allow_do_command. This can differ
240 * from CanSuspend() if the reason we are not allowed
241 * to execute a DoCommand is in squirrel and not the API.
242 * In that case use this function to restore the previous value.
243 * @return True iff DoCommands are allowed in the current scope.
245 static bool GetAllowDoCommand();
248 * Set the current company to execute commands for or request
250 * @param company The new company.
252 static void SetCompany(CompanyID company
);
255 * Get the current company we are executing commands for or
256 * requesting information about.
257 * @return The current company.
259 static CompanyID
GetCompany();
262 * Get the root company, the company that the script really
264 * @return The root company.
266 static CompanyID
GetRootCompany();
269 * Set the cost of the last command.
271 static void SetLastCost(Money last_cost
);
274 * Get the cost of the last command.
276 static Money
GetLastCost();
279 * Set a variable that can be used by callback functions to pass information.
281 static void SetCallbackVariable(int index
, int value
);
284 * Get the variable that is used by callback functions to pass information.
286 static int GetCallbackVariable(int index
);
289 * Can we suspend the script at this moment?
291 static bool CanSuspend();
294 * Get the pointer to store event data in.
296 static void *&GetEventPointer();
299 * Get the pointer to store log message in.
301 static ScriptLogTypes::LogData
&GetLogData();
304 * Get an allocated string with all control codes stripped off.
306 static std::string
GetString(StringID string
);
309 /* Helper functions for DoCommand. */
310 static std::tuple
<bool, bool, bool, bool> DoCommandPrep();
311 static bool DoCommandProcessResult(const CommandCost
&res
, Script_SuspendCallbackProc
*callback
, bool estimate_only
, bool asynchronous
);
312 static CommandCallbackData
*GetDoCommandCallback();
313 static Randomizer random_states
[OWNER_END
]; ///< Random states for each of the scripts (game script uses OWNER_DEITY)
316 namespace ScriptObjectInternal
{
317 /** Validate a single string argument coming from network. */
319 static inline void SanitizeSingleStringHelper(T
&data
)
321 if constexpr (std::is_same_v
<std::string
, T
>) {
322 /* The string must be valid, i.e. not contain special codes. Since some
323 * can be made with GSText, make sure the control codes are removed. */
324 data
= ::StrMakeValid(data
, SVS_NONE
);
328 /** Helper function to perform validation on command data strings. */
329 template<class Ttuple
, size_t... Tindices
>
330 static inline void SanitizeStringsHelper(Ttuple
&values
, std::index_sequence
<Tindices
...>)
332 ((SanitizeSingleStringHelper(std::get
<Tindices
>(values
))), ...);
335 /** Helper to process a single ClientID argument. */
337 static inline void SetClientIdHelper(T
&data
)
339 if constexpr (std::is_same_v
<ClientID
, T
>) {
340 if (data
== INVALID_CLIENT_ID
) data
= (ClientID
)UINT32_MAX
;
344 /** Set all invalid ClientID's to the proper value. */
345 template<class Ttuple
, size_t... Tindices
>
346 static inline void SetClientIds(Ttuple
&values
, std::index_sequence
<Tindices
...>)
348 ((SetClientIdHelper(std::get
<Tindices
>(values
))), ...);
351 /** Remove the first element of a tuple. */
352 template <template <typename
...> typename Tt
, typename T1
, typename
... Ts
>
353 static inline Tt
<Ts
...> RemoveFirstTupleElement(const Tt
<T1
, Ts
...> &tuple
)
355 return std::apply([](auto &&, const auto&... args
) { return std::tie(args
...); }, tuple
);
359 template <Commands Tcmd
, typename Tret
, typename
... Targs
>
360 bool ScriptObject::ScriptDoCommandHelper
<Tcmd
, Tret(*)(DoCommandFlag
, Targs
...)>::Execute(Script_SuspendCallbackProc
*callback
, std::tuple
<Targs
...> args
)
362 auto [err
, estimate_only
, asynchronous
, networking
] = ScriptObject::DoCommandPrep();
363 if (err
) return false;
365 if ((::GetCommandFlags
<Tcmd
>() & CMD_STR_CTRL
) == 0) {
366 ScriptObjectInternal::SanitizeStringsHelper(args
, std::index_sequence_for
<Targs
...>{});
370 if constexpr (std::is_same_v
<TileIndex
, std::tuple_element_t
<0, decltype(args
)>>) {
371 tile
= std::get
<0>(args
);
374 /* Do not even think about executing out-of-bounds tile-commands. */
375 if (tile
!= 0 && (tile
>= Map::Size() || (!IsValidTile(tile
) && (GetCommandFlags
<Tcmd
>() & CMD_ALL_TILES
) == 0))) return false;
377 /* Only set ClientID parameters when the command does not come from the network. */
378 if constexpr ((::GetCommandFlags
<Tcmd
>() & CMD_CLIENT_ID
) != 0) ScriptObjectInternal::SetClientIds(args
, std::index_sequence_for
<Targs
...>{});
380 /* Store the command for command callback validation. */
381 if (!estimate_only
&& networking
) ScriptObject::SetLastCommand(EndianBufferWriter
<CommandDataBuffer
>::FromValue(args
), Tcmd
);
383 /* Try to perform the command. */
384 Tret res
= ::Command
<Tcmd
>::Unsafe((StringID
)0, networking
? ScriptObject::GetDoCommandCallback() : nullptr, false, estimate_only
, tile
, args
);
386 if constexpr (std::is_same_v
<Tret
, CommandCost
>) {
387 return ScriptObject::DoCommandProcessResult(res
, callback
, estimate_only
, asynchronous
);
389 ScriptObject::SetLastCommandResData(EndianBufferWriter
<CommandDataBuffer
>::FromValue(ScriptObjectInternal::RemoveFirstTupleElement(res
)));
390 return ScriptObject::DoCommandProcessResult(std::get
<0>(res
), callback
, estimate_only
, asynchronous
);
395 * Internally used class to automate the ScriptObject reference counting.
398 template <typename T
>
399 class ScriptObjectRef
{
401 T
*data
; ///< The reference counted object.
404 * Create the reference counter for the given ScriptObject instance.
405 * @param data The underlying object.
407 ScriptObjectRef(T
*data
) : data(data
)
409 this->data
->AddRef();
412 /* No copy constructor. */
413 ScriptObjectRef(const ScriptObjectRef
<T
> &ref
) = delete;
415 /* Move constructor. */
416 ScriptObjectRef(ScriptObjectRef
<T
> &&ref
) noexcept
: data(std::exchange(ref
.data
, nullptr))
420 /* No copy assignment. */
421 ScriptObjectRef
& operator=(const ScriptObjectRef
<T
> &other
) = delete;
423 /* Move assignment. */
424 ScriptObjectRef
& operator=(ScriptObjectRef
<T
> &&other
) noexcept
426 std::swap(this->data
, other
.data
);
431 * Release the reference counted object.
435 if (this->data
!= nullptr) this->data
->Release();
439 * Dereferencing this reference returns a reference to the reference
441 * @return Reference to the underlying object.
449 * The arrow operator on this reference returns the reference counted object.
450 * @return Pointer to the underlying object.
458 #endif /* SCRIPT_OBJECT_HPP */