Files
@ r26123:a265ab51722a
Branch filter:
Location: cpp/openttd-patchpack/source/src/script/api/script_object.hpp
r26123:a265ab51722a
11.8 KiB
text/x-c++hdr
Codechange: Add support for additional command result values.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 | /*
* This file is part of OpenTTD.
* 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.
* 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.
* 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/>.
*/
/** @file script_object.hpp Main object, on which all objects depend. */
#ifndef SCRIPT_OBJECT_HPP
#define SCRIPT_OBJECT_HPP
#include "../../misc/countedptr.hpp"
#include "../../road_type.h"
#include "../../rail_type.h"
#include "../../string_func.h"
#include "../../command_func.h"
#include "script_types.hpp"
#include "../script_suspend.hpp"
#include "../squirrel.hpp"
/**
* The callback function for Mode-classes.
*/
typedef bool (ScriptModeProc)();
/**
* Uper-parent object of all API classes. You should never use this class in
* your script, as it doesn't publish any public functions. It is used
* internally to have a common place to handle general things, like internal
* command processing, and command-validation checks.
* @api none
*/
class ScriptObject : public SimpleCountedObject {
friend class ScriptInstance;
friend class ScriptController;
protected:
/**
* A class that handles the current active instance. By instantiating it at
* the beginning of a function with the current active instance, it remains
* active till the scope of the variable closes. It then automatically
* reverts to the active instance it was before instantiating.
*/
class ActiveInstance {
friend class ScriptObject;
public:
ActiveInstance(ScriptInstance *instance);
~ActiveInstance();
private:
ScriptInstance *last_active; ///< The active instance before we go instantiated.
ScriptAllocatorScope alc_scope; ///< Keep the correct allocator for the script instance activated
static ScriptInstance *active; ///< The global current active instance.
};
public:
/**
* Store the latest result of a DoCommand per company.
* @param res The result of the last command.
*/
static void SetLastCommandRes(bool res);
/**
* Store the extra data return by the last DoCommand.
* @param data Extra data return by the command.
*/
static void SetLastCommandResData(CommandDataBuffer data);
/**
* Get the currently active instance.
* @return The instance.
*/
static class ScriptInstance *GetActiveInstance();
protected:
template<Commands TCmd, typename T> struct ScriptDoCommandHelper;
/**
* Templated wrapper that exposes the command parameter arguments
* on the various DoCommand calls.
* @tparam Tcmd The command-id to execute.
* @tparam Tret Return type of the command.
* @tparam Targs The command parameter types.
*/
template <Commands Tcmd, typename Tret, typename... Targs>
struct ScriptDoCommandHelper<Tcmd, Tret(*)(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.
*/
static void SetLastCommand(TileIndex tile, const CommandDataBuffer &data, Commands cmd);
/**
* Check if it's the latest command executed by the script.
*/
static bool CheckLastCommand(TileIndex tile, const CommandDataBuffer &data, Commands cmd);
/**
* Sets the DoCommand costs counter to a value.
*/
static void SetDoCommandCosts(Money value);
/**
* Increase the current value of the DoCommand costs counter.
*/
static void IncreaseDoCommandCosts(Money value);
/**
* Get the current DoCommand costs counter.
*/
static Money GetDoCommandCosts();
/**
* Set the DoCommand last error.
*/
static void SetLastError(ScriptErrorType last_error);
/**
* Get the DoCommand last error.
*/
static ScriptErrorType GetLastError();
/**
* Set the road type.
*/
static void SetRoadType(RoadType road_type);
/**
* Get the road type.
*/
static RoadType GetRoadType();
/**
* Set the rail type.
*/
static void SetRailType(RailType rail_type);
/**
* Get the rail type.
*/
static RailType GetRailType();
/**
* Set the current mode of your script to this proc.
*/
static void SetDoCommandMode(ScriptModeProc *proc, ScriptObject *instance);
/**
* Get the current mode your script is currently under.
*/
static ScriptModeProc *GetDoCommandMode();
/**
* Get the instance of the current mode your script is currently under.
*/
static ScriptObject *GetDoCommandModeInstance();
/**
* Set the delay of the DoCommand.
*/
static void SetDoCommandDelay(uint ticks);
/**
* Get the delay of the DoCommand.
*/
static uint GetDoCommandDelay();
/**
* Get the latest result of a DoCommand.
*/
static bool GetLastCommandRes();
/**
* Get the extra return data from the last DoCommand.
*/
static const CommandDataBuffer &GetLastCommandResData();
/**
* Get the latest stored new_vehicle_id.
*/
static VehicleID GetNewVehicleID();
/**
* Get the latest stored new_sign_id.
*/
static SignID GetNewSignID();
/**
* Get the latest stored new_group_id.
*/
static GroupID GetNewGroupID();
/**
* Get the latest stored new_goal_id.
*/
static GoalID GetNewGoalID();
/**
* Get the latest stored new_story_page_id.
*/
static StoryPageID GetNewStoryPageID();
/**
* Get the latest stored new_story_page_id.
*/
static StoryPageID GetNewStoryPageElementID();
/**
* Store a allow_do_command per company.
* @param allow The new allow.
*/
static void SetAllowDoCommand(bool allow);
/**
* Get the internal value of allow_do_command. This can differ
* from CanSuspend() if the reason we are not allowed
* to execute a DoCommand is in squirrel and not the API.
* In that case use this function to restore the previous value.
* @return True iff DoCommands are allowed in the current scope.
*/
static bool GetAllowDoCommand();
/**
* Set the current company to execute commands for or request
* information about.
* @param company The new company.
*/
static void SetCompany(CompanyID company);
/**
* Get the current company we are executing commands for or
* requesting information about.
* @return The current company.
*/
static CompanyID GetCompany();
/**
* Get the root company, the company that the script really
* runs under / for.
* @return The root company.
*/
static CompanyID GetRootCompany();
/**
* Set the cost of the last command.
*/
static void SetLastCost(Money last_cost);
/**
* Get the cost of the last command.
*/
static Money GetLastCost();
/**
* Set a variable that can be used by callback functions to pass information.
*/
static void SetCallbackVariable(int index, int value);
/**
* Get the variable that is used by callback functions to pass information.
*/
static int GetCallbackVariable(int index);
/**
* Can we suspend the script at this moment?
*/
static bool CanSuspend();
/**
* Get the pointer to store event data in.
*/
static void *&GetEventPointer();
/**
* Get the pointer to store log message in.
*/
static void *&GetLogPointer();
/**
* Get an allocated string with all control codes stripped off.
*/
static char *GetString(StringID string);
private:
/**
* Store a new_vehicle_id per company.
* @param vehicle_id The new VehicleID.
*/
static void SetNewVehicleID(VehicleID vehicle_id);
/**
* Store a new_sign_id per company.
* @param sign_id The new SignID.
*/
static void SetNewSignID(SignID sign_id);
/**
* Store a new_group_id per company.
* @param group_id The new GroupID.
*/
static void SetNewGroupID(GroupID group_id);
/**
* Store a new_goal_id per company.
* @param goal_id The new GoalID.
*/
static void SetNewGoalID(GoalID goal_id);
/**
* Store a new_story_page_id per company.
* @param story_page_id The new StoryPageID.
*/
static void SetNewStoryPageID(StoryPageID story_page_id);
/**
* Store a new_story_page_id per company.
* @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 CommandCallbackData *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))), ...);
}
/** Helper to process a single ClientID argument. */
template <class T>
static inline void SetClientIdHelper(T &data)
{
if constexpr (std::is_same_v<ClientID, T>) {
if (data == INVALID_CLIENT_ID) data = (ClientID)UINT32_MAX;
}
}
/** Set all invalid ClientID's to the proper value. */
template<class Ttuple, size_t... Tindices>
static inline void SetClientIds(Ttuple &values, std::index_sequence<Tindices...>)
{
((SetClientIdHelper(std::get<Tindices>(values))), ...);
}
/** Remove the first element of a tuple. */
template <template <typename...> typename Tt, typename T1, typename... Ts>
static inline Tt<Ts...> RemoveFirstTupleElement(const Tt<T1, Ts...> &tuple)
{
return std::apply([](auto &&, const auto&... args) { return std::tie(args...); }, tuple);
}
}
template <Commands Tcmd, typename Tret, typename... Targs>
bool ScriptObject::ScriptDoCommandHelper<Tcmd, Tret(*)(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 ClientID parameters when the command does not come from the network. */
if constexpr ((::GetCommandFlags<Tcmd>() & CMD_CLIENT_ID) != 0) ScriptObjectInternal::SetClientIds(args, std::index_sequence_for<Targs...>{});
/* Store the command for command callback validation. */
if (!estimate_only && networking) ScriptObject::SetLastCommand(tile, EndianBufferWriter<CommandDataBuffer>::FromValue(args), Tcmd);
/* Try to perform the command. */
Tret res = ::Command<Tcmd>::Unsafe((StringID)0, networking ? ScriptObject::GetDoCommandCallback() : nullptr, false, estimate_only, tile, args);
if constexpr (std::is_same_v<Tret, CommandCost>) {
return ScriptObject::DoCommandProcessResult(res, callback, estimate_only);
} else {
ScriptObject::SetLastCommandResData(EndianBufferWriter<CommandDataBuffer>::FromValue(ScriptObjectInternal::RemoveFirstTupleElement(res)));
return ScriptObject::DoCommandProcessResult(std::get<0>(res), callback, estimate_only);
}
}
#endif /* SCRIPT_OBJECT_HPP */
|