Update: Translations from eints
[openttd-github.git] / src / script / api / script_object.hpp
blobc6533991df39d50f8a8351194dc9fdd73b79008f
1 /*
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/>.
6 */
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"
24 #include <utility>
26 /**
27 * The callback function for Mode-classes.
29 typedef bool (ScriptModeProc)();
31 /**
32 * The callback function for Async Mode-classes.
34 typedef bool (ScriptAsyncModeProc)();
36 /**
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.
42 * @api -all
44 class SimpleCountedObject {
45 public:
46 SimpleCountedObject() : ref_count(0) {}
47 virtual ~SimpleCountedObject() = default;
49 inline void AddRef() { ++this->ref_count; }
50 void Release();
51 virtual void FinalRelease() {};
53 private:
54 int32_t ref_count;
57 /**
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.
62 * @api none
64 class ScriptObject : public SimpleCountedObject {
65 friend class ScriptInstance;
66 friend class ScriptController;
67 friend class TestScriptController;
68 protected:
69 /**
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;
77 public:
78 ActiveInstance(ScriptInstance *instance);
79 ~ActiveInstance();
80 private:
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.
87 public:
88 /**
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);
94 /**
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();
118 protected:
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...));
140 private:
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();
183 * Set the road type.
185 static void SetRoadType(RoadType road_type);
188 * Get the road type.
190 static RoadType GetRoadType();
193 * Set the rail type.
195 static void SetRailType(RailType rail_type);
198 * Get the 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
269 * information about.
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
283 * runs under / for.
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);
328 private:
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. */
338 template <class T>
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. */
356 template <class T>
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...>{});
389 TileIndex tile{};
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);
408 } else {
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.
416 * @api -all
418 template <typename T>
419 class ScriptObjectRef {
420 private:
421 T *data; ///< The reference counted object.
422 public:
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);
447 return *this;
451 * Release the reference counted object.
453 ~ScriptObjectRef()
455 if (this->data != nullptr) this->data->Release();
459 * Dereferencing this reference returns a reference to the reference
460 * counted object
461 * @return Reference to the underlying object.
463 T &operator*()
465 return *this->data;
469 * The arrow operator on this reference returns the reference counted object.
470 * @return Pointer to the underlying object.
472 T *operator->()
474 return this->data;
478 * The arrow operator on this reference returns the reference counted object.
479 * @return Pointer to the underlying object.
481 const T *operator->() const
483 return this->data;
487 #endif /* SCRIPT_OBJECT_HPP */