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 "../../road_type.h"
14 #include "../../rail_type.h"
15 #include "../../string_func.h"
16 #include "../../command_func.h"
17 #include "../../core/random_func.hpp"
19 #include "script_types.hpp"
20 #include "script_log_types.hpp"
21 #include "../script_suspend.hpp"
22 #include "../squirrel.hpp"
27 * The callback function for Mode-classes.
29 typedef bool (ScriptModeProc
)();
32 * The callback function for Async Mode-classes.
34 typedef bool (ScriptAsyncModeProc
)();
37 * Simple counted object. Use it as base of your struct/class if you want to use
38 * basic reference counting. Your struct/class will destroy and free itself when
39 * last reference to it is released (using Release() method). The initial reference
40 * count (when it is created) is zero (don't forget AddRef() at least one time if
41 * not using ScriptObjectRef.
44 class SimpleCountedObject
{
46 SimpleCountedObject() : ref_count(0) {}
47 virtual ~SimpleCountedObject() = default;
49 inline void AddRef() { ++this->ref_count
; }
51 virtual void FinalRelease() {};
58 * Uper-parent object of all API classes. You should never use this class in
59 * your script, as it doesn't publish any public functions. It is used
60 * internally to have a common place to handle general things, like internal
61 * command processing, and command-validation checks.
64 class ScriptObject
: public SimpleCountedObject
{
65 friend class ScriptInstance
;
66 friend class ScriptController
;
67 friend class TestScriptController
;
70 * A class that handles the current active instance. By instantiating it at
71 * the beginning of a function with the current active instance, it remains
72 * active till the scope of the variable closes. It then automatically
73 * reverts to the active instance it was before instantiating.
75 class ActiveInstance
{
76 friend class ScriptObject
;
78 ActiveInstance(ScriptInstance
*instance
);
81 ScriptInstance
*last_active
; ///< The active instance before we go instantiated.
82 ScriptAllocatorScope alc_scope
; ///< Keep the correct allocator for the script instance activated
84 static ScriptInstance
*active
; ///< The global current active instance.
89 * Store the latest result of a DoCommand per company.
90 * @param res The result of the last command.
92 static void SetLastCommandRes(bool res
);
95 * Store the extra data return by the last DoCommand.
96 * @param data Extra data return by the command.
98 static void SetLastCommandResData(CommandDataBuffer data
);
101 * Get the currently active instance.
102 * @return The instance.
104 static class ScriptInstance
*GetActiveInstance();
107 * Get a reference of the randomizer that brings this script random values.
108 * @param owner The owner/script to get the randomizer for. This defaults to ScriptObject::GetRootCompany()
110 static Randomizer
&GetRandomizer(Owner owner
= ScriptObject::GetRootCompany());
113 * Initialize/reset the script random states. The state of the scripts are
114 * based on the current _random seed, but _random does not get changed.
116 static void InitializeRandomizers();
119 template <Commands TCmd
, typename T
> struct ScriptDoCommandHelper
;
122 * Templated wrapper that exposes the command parameter arguments
123 * on the various DoCommand calls.
124 * @tparam Tcmd The command-id to execute.
125 * @tparam Tret Return type of the command.
126 * @tparam Targs The command parameter types.
128 template <Commands Tcmd
, typename Tret
, typename
... Targs
>
129 struct ScriptDoCommandHelper
<Tcmd
, Tret(*)(DoCommandFlag
, Targs
...)> {
130 static bool Do(Script_SuspendCallbackProc
*callback
, Targs
... args
)
132 return Execute(callback
, std::forward_as_tuple(args
...));
135 static bool Do(Targs
... args
)
137 return Execute(nullptr, std::forward_as_tuple(args
...));
141 static bool Execute(Script_SuspendCallbackProc
*callback
, std::tuple
<Targs
...> args
);
144 template <Commands Tcmd
>
145 using Command
= ScriptDoCommandHelper
<Tcmd
, typename ::CommandTraits
<Tcmd
>::ProcType
>;
148 * Store the latest command executed by the script.
150 static void SetLastCommand(const CommandDataBuffer
&data
, Commands cmd
);
153 * Check if it's the latest command executed by the script.
155 static bool CheckLastCommand(const CommandDataBuffer
&data
, Commands cmd
);
158 * Sets the DoCommand costs counter to a value.
160 static void SetDoCommandCosts(Money value
);
163 * Increase the current value of the DoCommand costs counter.
165 static void IncreaseDoCommandCosts(Money value
);
168 * Get the current DoCommand costs counter.
170 static Money
GetDoCommandCosts();
173 * Set the DoCommand last error.
175 static void SetLastError(ScriptErrorType last_error
);
178 * Get the DoCommand last error.
180 static ScriptErrorType
GetLastError();
185 static void SetRoadType(RoadType road_type
);
190 static RoadType
GetRoadType();
195 static void SetRailType(RailType rail_type
);
200 static RailType
GetRailType();
203 * Set the current mode of your script to this proc.
205 static void SetDoCommandMode(ScriptModeProc
*proc
, ScriptObject
*instance
);
208 * Get the current mode your script is currently under.
210 static ScriptModeProc
*GetDoCommandMode();
213 * Get the instance of the current mode your script is currently under.
215 static ScriptObject
*GetDoCommandModeInstance();
218 * Set the current async mode of your script to this proc.
220 static void SetDoCommandAsyncMode(ScriptAsyncModeProc
*proc
, ScriptObject
*instance
);
223 * Get the current async mode your script is currently under.
225 static ScriptModeProc
*GetDoCommandAsyncMode();
228 * Get the instance of the current async mode your script is currently under.
230 static ScriptObject
*GetDoCommandAsyncModeInstance();
233 * Set the delay of the DoCommand.
235 static void SetDoCommandDelay(uint ticks
);
238 * Get the delay of the DoCommand.
240 static uint
GetDoCommandDelay();
243 * Get the latest result of a DoCommand.
245 static bool GetLastCommandRes();
248 * Get the extra return data from the last DoCommand.
250 static const CommandDataBuffer
&GetLastCommandResData();
253 * Store a allow_do_command per company.
254 * @param allow The new allow.
256 static void SetAllowDoCommand(bool allow
);
259 * Get the internal value of allow_do_command. This can differ
260 * from CanSuspend() if the reason we are not allowed
261 * to execute a DoCommand is in squirrel and not the API.
262 * In that case use this function to restore the previous value.
263 * @return True iff DoCommands are allowed in the current scope.
265 static bool GetAllowDoCommand();
268 * Set the current company to execute commands for or request
270 * @param company The new company.
272 static void SetCompany(CompanyID company
);
275 * Get the current company we are executing commands for or
276 * requesting information about.
277 * @return The current company.
279 static CompanyID
GetCompany();
282 * Get the root company, the company that the script really
284 * @return The root company.
286 static CompanyID
GetRootCompany();
289 * Set the cost of the last command.
291 static void SetLastCost(Money last_cost
);
294 * Get the cost of the last command.
296 static Money
GetLastCost();
299 * Set a variable that can be used by callback functions to pass information.
301 static void SetCallbackVariable(int index
, int value
);
304 * Get the variable that is used by callback functions to pass information.
306 static int GetCallbackVariable(int index
);
309 * Can we suspend the script at this moment?
311 static bool CanSuspend();
314 * Get the pointer to store event data in.
316 static void *&GetEventPointer();
319 * Get the pointer to store log message in.
321 static ScriptLogTypes::LogData
&GetLogData();
324 * Get an allocated string with all control codes stripped off.
326 static std::string
GetString(StringID string
);
329 /* Helper functions for DoCommand. */
330 static std::tuple
<bool, bool, bool, bool> DoCommandPrep();
331 static bool DoCommandProcessResult(const CommandCost
&res
, Script_SuspendCallbackProc
*callback
, bool estimate_only
, bool asynchronous
);
332 static CommandCallbackData
*GetDoCommandCallback();
333 static Randomizer random_states
[OWNER_END
]; ///< Random states for each of the scripts (game script uses OWNER_DEITY)
336 namespace ScriptObjectInternal
{
337 /** Validate a single string argument coming from network. */
339 static inline void SanitizeSingleStringHelper(T
&data
)
341 if constexpr (std::is_same_v
<std::string
, T
>) {
342 /* The string must be valid, i.e. not contain special codes. Since some
343 * can be made with GSText, make sure the control codes are removed. */
344 data
= ::StrMakeValid(data
, SVS_NONE
);
348 /** Helper function to perform validation on command data strings. */
349 template <class Ttuple
, size_t... Tindices
>
350 static inline void SanitizeStringsHelper(Ttuple
&values
, std::index_sequence
<Tindices
...>)
352 ((SanitizeSingleStringHelper(std::get
<Tindices
>(values
))), ...);
355 /** Helper to process a single ClientID argument. */
357 static inline void SetClientIdHelper(T
&data
)
359 if constexpr (std::is_same_v
<ClientID
, T
>) {
360 if (data
== INVALID_CLIENT_ID
) data
= (ClientID
)UINT32_MAX
;
364 /** Set all invalid ClientID's to the proper value. */
365 template <class Ttuple
, size_t... Tindices
>
366 static inline void SetClientIds(Ttuple
&values
, std::index_sequence
<Tindices
...>)
368 ((SetClientIdHelper(std::get
<Tindices
>(values
))), ...);
371 /** Remove the first element of a tuple. */
372 template <template <typename
...> typename Tt
, typename T1
, typename
... Ts
>
373 static inline Tt
<Ts
...> RemoveFirstTupleElement(const Tt
<T1
, Ts
...> &tuple
)
375 return std::apply([](auto &&, const auto&... args
) { return std::tie(args
...); }, tuple
);
379 template <Commands Tcmd
, typename Tret
, typename
... Targs
>
380 bool ScriptObject::ScriptDoCommandHelper
<Tcmd
, Tret(*)(DoCommandFlag
, Targs
...)>::Execute(Script_SuspendCallbackProc
*callback
, std::tuple
<Targs
...> args
)
382 auto [err
, estimate_only
, asynchronous
, networking
] = ScriptObject::DoCommandPrep();
383 if (err
) return false;
385 if ((::GetCommandFlags
<Tcmd
>() & CMD_STR_CTRL
) == 0) {
386 ScriptObjectInternal::SanitizeStringsHelper(args
, std::index_sequence_for
<Targs
...>{});
390 if constexpr (std::is_same_v
<TileIndex
, std::tuple_element_t
<0, decltype(args
)>>) {
391 tile
= std::get
<0>(args
);
394 /* Do not even think about executing out-of-bounds tile-commands. */
395 if (tile
!= 0 && (tile
>= Map::Size() || (!IsValidTile(tile
) && (GetCommandFlags
<Tcmd
>() & CMD_ALL_TILES
) == 0))) return false;
397 /* Only set ClientID parameters when the command does not come from the network. */
398 if constexpr ((::GetCommandFlags
<Tcmd
>() & CMD_CLIENT_ID
) != 0) ScriptObjectInternal::SetClientIds(args
, std::index_sequence_for
<Targs
...>{});
400 /* Store the command for command callback validation. */
401 if (!estimate_only
&& networking
) ScriptObject::SetLastCommand(EndianBufferWriter
<CommandDataBuffer
>::FromValue(args
), Tcmd
);
403 /* Try to perform the command. */
404 Tret res
= ::Command
<Tcmd
>::Unsafe((StringID
)0, networking
? ScriptObject::GetDoCommandCallback() : nullptr, false, estimate_only
, tile
, args
);
406 if constexpr (std::is_same_v
<Tret
, CommandCost
>) {
407 return ScriptObject::DoCommandProcessResult(res
, callback
, estimate_only
, asynchronous
);
409 ScriptObject::SetLastCommandResData(EndianBufferWriter
<CommandDataBuffer
>::FromValue(ScriptObjectInternal::RemoveFirstTupleElement(res
)));
410 return ScriptObject::DoCommandProcessResult(std::get
<0>(res
), callback
, estimate_only
, asynchronous
);
415 * Internally used class to automate the ScriptObject reference counting.
418 template <typename T
>
419 class ScriptObjectRef
{
421 T
*data
; ///< The reference counted object.
424 * Create the reference counter for the given ScriptObject instance.
425 * @param data The underlying object.
427 ScriptObjectRef(T
*data
) : data(data
)
429 if (this->data
!= nullptr) this->data
->AddRef();
432 /* No copy constructor. */
433 ScriptObjectRef(const ScriptObjectRef
<T
> &ref
) = delete;
435 /* Move constructor. */
436 ScriptObjectRef(ScriptObjectRef
<T
> &&ref
) noexcept
: data(std::exchange(ref
.data
, nullptr))
440 /* No copy assignment. */
441 ScriptObjectRef
& operator=(const ScriptObjectRef
<T
> &other
) = delete;
443 /* Move assignment. */
444 ScriptObjectRef
& operator=(ScriptObjectRef
<T
> &&other
) noexcept
446 std::swap(this->data
, other
.data
);
451 * Release the reference counted object.
455 if (this->data
!= nullptr) this->data
->Release();
459 * Dereferencing this reference returns a reference to the reference
461 * @return Reference to the underlying object.
469 * The arrow operator on this reference returns the reference counted object.
470 * @return Pointer to the underlying object.
478 * The arrow operator on this reference returns the reference counted object.
479 * @return Pointer to the underlying object.
481 const T
*operator->() const
487 #endif /* SCRIPT_OBJECT_HPP */