1 /***************************************************************************** 2 * Copyright (c) 2014-2020 OpenRCT2 developers 3 * 4 * For a complete list of all authors, please refer to contributors.md 5 * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 6 * 7 * OpenRCT2 is licensed under the GNU General Public License version 3. 8 *****************************************************************************/ 9 10 #pragma once 11 12 #ifdef ENABLE_SCRIPTING 13 14 # include "../core/Console.hpp" 15 # include "../core/EnumMap.hpp" 16 # include "../ride/Vehicle.h" 17 # include "../world/Map.h" 18 19 # include <cstdio> 20 # include <dukglue/dukglue.h> 21 # include <duktape.h> 22 # include <optional> 23 # include <stdexcept> 24 25 namespace OpenRCT2::Scripting 26 { GetObjectAsDukValue(duk_context * ctx,const std::shared_ptr<T> & value)27 template<typename T> DukValue GetObjectAsDukValue(duk_context* ctx, const std::shared_ptr<T>& value) 28 { 29 dukglue::types::DukType<std::shared_ptr<T>>::template push<T>(ctx, value); 30 return DukValue::take_from_stack(ctx); 31 } 32 33 template<typename T> T AsOrDefault(const DukValue& value, const T& defaultValue = {}) = delete; 34 AsOrDefault(const DukValue & value,std::string_view defaultValue)35 inline std::string AsOrDefault(const DukValue& value, std::string_view defaultValue) 36 { 37 return value.type() == DukValue::STRING ? value.as_string() : std::string(defaultValue); 38 } 39 AsOrDefault(const DukValue & value,const std::string & defaultValue)40 template<> inline std::string AsOrDefault(const DukValue& value, const std::string& defaultValue) 41 { 42 return value.type() == DukValue::STRING ? value.as_string() : defaultValue; 43 } 44 AsOrDefault(const DukValue & value,const int32_t & defaultValue)45 template<> inline int32_t AsOrDefault(const DukValue& value, const int32_t& defaultValue) 46 { 47 return value.type() == DukValue::NUMBER ? value.as_int() : defaultValue; 48 } 49 AsOrDefault(const DukValue & value,const bool & defaultValue)50 template<> inline bool AsOrDefault(const DukValue& value, const bool& defaultValue) 51 { 52 return value.type() == DukValue::BOOLEAN ? value.as_bool() : defaultValue; 53 } 54 55 enum class DukUndefined 56 { 57 }; 58 constexpr DukUndefined undefined{}; 59 60 /** 61 * Allows creation of an object on the duktape stack and setting properties on it before 62 * retrieving the DukValue instance of it. 63 */ 64 class DukObject 65 { 66 private: 67 duk_context* _ctx{}; 68 duk_idx_t _idx = DUK_INVALID_INDEX; 69 70 public: DukObject(duk_context * ctx)71 DukObject(duk_context* ctx) 72 : _ctx(ctx) 73 { 74 } 75 76 DukObject(const DukObject&) = delete; 77 DukObject(DukObject && m)78 DukObject(DukObject&& m) noexcept 79 { 80 _ctx = m._ctx; 81 _idx = m._idx; 82 m._ctx = {}; 83 m._idx = {}; 84 } 85 ~DukObject()86 ~DukObject() 87 { 88 PopObjectIfExists(); 89 } 90 Set(const char * name,std::nullptr_t)91 void Set(const char* name, std::nullptr_t) 92 { 93 EnsureObjectPushed(); 94 duk_push_null(_ctx); 95 duk_put_prop_string(_ctx, _idx, name); 96 } 97 Set(const char * name,DukUndefined)98 void Set(const char* name, DukUndefined) 99 { 100 EnsureObjectPushed(); 101 duk_push_undefined(_ctx); 102 duk_put_prop_string(_ctx, _idx, name); 103 } 104 Set(const char * name,bool value)105 void Set(const char* name, bool value) 106 { 107 EnsureObjectPushed(); 108 duk_push_boolean(_ctx, value); 109 duk_put_prop_string(_ctx, _idx, name); 110 } 111 Set(const char * name,int32_t value)112 void Set(const char* name, int32_t value) 113 { 114 EnsureObjectPushed(); 115 duk_push_int(_ctx, value); 116 duk_put_prop_string(_ctx, _idx, name); 117 } 118 Set(const char * name,uint32_t value)119 void Set(const char* name, uint32_t value) 120 { 121 EnsureObjectPushed(); 122 duk_push_uint(_ctx, value); 123 duk_put_prop_string(_ctx, _idx, name); 124 } 125 Set(const char * name,int64_t value)126 void Set(const char* name, int64_t value) 127 { 128 EnsureObjectPushed(); 129 duk_push_number(_ctx, value); 130 duk_put_prop_string(_ctx, _idx, name); 131 } 132 Set(const char * name,uint64_t value)133 void Set(const char* name, uint64_t value) 134 { 135 EnsureObjectPushed(); 136 duk_push_number(_ctx, value); 137 duk_put_prop_string(_ctx, _idx, name); 138 } 139 Set(const char * name,std::string_view value)140 void Set(const char* name, std::string_view value) 141 { 142 EnsureObjectPushed(); 143 duk_push_lstring(_ctx, value.data(), value.size()); 144 duk_put_prop_string(_ctx, _idx, name); 145 } 146 Set(const char * name,const char * value)147 void Set(const char* name, const char* value) 148 { 149 Set(name, std::string_view(value)); 150 } 151 Set(const char * name,const DukValue & value)152 void Set(const char* name, const DukValue& value) 153 { 154 EnsureObjectPushed(); 155 value.push(); 156 duk_put_prop_string(_ctx, _idx, name); 157 } 158 Set(const char * name,const std::optional<T> & value)159 template<typename T> void Set(const char* name, const std::optional<T>& value) 160 { 161 if (value) 162 { 163 EnsureObjectPushed(); 164 duk_push_null(_ctx); 165 duk_put_prop_string(_ctx, _idx, name); 166 } 167 else 168 { 169 Set(name, *value); 170 } 171 } 172 Take()173 DukValue Take() 174 { 175 EnsureObjectPushed(); 176 auto result = DukValue::take_from_stack(_ctx, _idx); 177 _idx = DUK_INVALID_INDEX; 178 return result; 179 } 180 181 private: PopObjectIfExists()182 void PopObjectIfExists() 183 { 184 if (_idx != DUK_INVALID_INDEX) 185 { 186 duk_remove(_ctx, _idx); 187 _idx = DUK_INVALID_INDEX; 188 } 189 } 190 EnsureObjectPushed()191 void EnsureObjectPushed() 192 { 193 if (_idx == DUK_INVALID_INDEX) 194 { 195 _idx = duk_push_object(_ctx); 196 } 197 } 198 }; 199 200 class DukStackFrame 201 { 202 private: 203 duk_context* _ctx{}; 204 duk_idx_t _top; 205 206 public: DukStackFrame(duk_context * ctx)207 DukStackFrame(duk_context* ctx) 208 : _ctx(ctx) 209 { 210 _top = duk_get_top(ctx); 211 } 212 ~DukStackFrame()213 ~DukStackFrame() 214 { 215 auto top = duk_get_top(_ctx); 216 if (top != _top) 217 { 218 duk_set_top(_ctx, _top); 219 _ctx = {}; 220 Console::Error::WriteLine("duktape stack was not returned to original state!"); 221 } 222 _ctx = {}; 223 } 224 225 DukStackFrame(const DukStackFrame&) = delete; 226 DukStackFrame(DukStackFrame&&) = delete; 227 }; 228 229 /** 230 * Bi-directional map for converting between strings and enums / numbers. 231 */ 232 template<typename T> using DukEnumMap = EnumMap<T>; 233 duk_json_decode_wrapper(duk_context * ctx,void *)234 inline duk_ret_t duk_json_decode_wrapper(duk_context* ctx, void*) 235 { 236 duk_json_decode(ctx, -1); 237 return 1; 238 } 239 DuktapeTryParseJson(duk_context * ctx,std::string_view json)240 inline std::optional<DukValue> DuktapeTryParseJson(duk_context* ctx, std::string_view json) 241 { 242 duk_push_lstring(ctx, json.data(), json.size()); 243 if (duk_safe_call(ctx, duk_json_decode_wrapper, nullptr, 1, 1) == DUK_EXEC_SUCCESS) 244 { 245 return DukValue::take_from_stack(ctx); 246 } 247 248 // Pop error off stack 249 duk_pop(ctx); 250 return std::nullopt; 251 } 252 253 std::string ProcessString(const DukValue& value); 254 255 template<typename T> DukValue ToDuk(duk_context* ctx, const T& value) = delete; 256 template<typename T> T FromDuk(const DukValue& s) = delete; 257 ToDuk(duk_context * ctx,const std::nullptr_t &)258 template<> inline DukValue ToDuk(duk_context* ctx, const std::nullptr_t&) 259 { 260 duk_push_null(ctx); 261 return DukValue::take_from_stack(ctx); 262 } 263 ToDuk(duk_context * ctx,const DukUndefined &)264 template<> inline DukValue ToDuk(duk_context* ctx, const DukUndefined&) 265 { 266 duk_push_undefined(ctx); 267 return DukValue::take_from_stack(ctx); 268 } 269 ToDuk(duk_context * ctx,const bool & value)270 template<> inline DukValue ToDuk(duk_context* ctx, const bool& value) 271 { 272 duk_push_boolean(ctx, value); 273 return DukValue::take_from_stack(ctx); 274 } 275 ToDuk(duk_context * ctx,const uint8_t & value)276 template<> inline DukValue ToDuk(duk_context* ctx, const uint8_t& value) 277 { 278 duk_push_int(ctx, value); 279 return DukValue::take_from_stack(ctx); 280 } 281 ToDuk(duk_context * ctx,const int32_t & value)282 template<> inline DukValue ToDuk(duk_context* ctx, const int32_t& value) 283 { 284 duk_push_int(ctx, value); 285 return DukValue::take_from_stack(ctx); 286 } 287 ToDuk(duk_context * ctx,const int64_t & value)288 template<> inline DukValue ToDuk(duk_context* ctx, const int64_t& value) 289 { 290 duk_push_number(ctx, value); 291 return DukValue::take_from_stack(ctx); 292 } 293 ToDuk(duk_context * ctx,const std::string_view & value)294 template<> inline DukValue ToDuk(duk_context* ctx, const std::string_view& value) 295 { 296 duk_push_lstring(ctx, value.data(), value.size()); 297 return DukValue::take_from_stack(ctx); 298 } 299 ToDuk(duk_context * ctx,const std::string & value)300 template<> inline DukValue ToDuk(duk_context* ctx, const std::string& value) 301 { 302 return ToDuk(ctx, std::string_view(value)); 303 } 304 ToDuk(duk_context * ctx,const char (& value)[TLen])305 template<size_t TLen> inline DukValue ToDuk(duk_context* ctx, const char (&value)[TLen]) 306 { 307 duk_push_string(ctx, value); 308 return DukValue::take_from_stack(ctx); 309 } 310 ToDuk(duk_context * ctx,const std::optional<T> & value)311 template<typename T> inline DukValue ToDuk(duk_context* ctx, const std::optional<T>& value) 312 { 313 return value ? ToDuk(ctx, *value) : ToDuk(ctx, nullptr); 314 } 315 FromDuk(const DukValue & d)316 template<> CoordsXY inline FromDuk(const DukValue& d) 317 { 318 CoordsXY result; 319 result.x = AsOrDefault(d["x"], 0); 320 result.y = AsOrDefault(d["y"], 0); 321 return result; 322 } 323 ToDuk(duk_context * ctx,const CoordsXY & coords)324 template<> DukValue inline ToDuk(duk_context* ctx, const CoordsXY& coords) 325 { 326 DukObject dukCoords(ctx); 327 dukCoords.Set("x", coords.x); 328 dukCoords.Set("y", coords.y); 329 return dukCoords.Take(); 330 } 331 ToDuk(duk_context * ctx,const ScreenCoordsXY & coords)332 template<> DukValue inline ToDuk(duk_context* ctx, const ScreenCoordsXY& coords) 333 { 334 DukObject dukCoords(ctx); 335 dukCoords.Set("x", coords.x); 336 dukCoords.Set("y", coords.y); 337 return dukCoords.Take(); 338 } 339 ToDuk(duk_context * ctx,const CoordsXYZ & value)340 template<> inline DukValue ToDuk(duk_context* ctx, const CoordsXYZ& value) 341 { 342 if (value.IsNull()) 343 { 344 return ToDuk(ctx, nullptr); 345 } 346 347 DukObject dukCoords(ctx); 348 dukCoords.Set("x", value.x); 349 dukCoords.Set("y", value.y); 350 dukCoords.Set("z", value.z); 351 return dukCoords.Take(); 352 } 353 FromDuk(const DukValue & value)354 template<> inline CoordsXYZ FromDuk(const DukValue& value) 355 { 356 CoordsXYZ result; 357 if (value.type() == DukValue::Type::OBJECT) 358 { 359 result.x = AsOrDefault(value["x"], 0); 360 result.y = AsOrDefault(value["y"], 0); 361 result.z = AsOrDefault(value["z"], 0); 362 } 363 else 364 { 365 result.SetNull(); 366 } 367 return result; 368 } 369 ToDuk(duk_context * ctx,const CoordsXYZD & value)370 template<> inline DukValue ToDuk(duk_context* ctx, const CoordsXYZD& value) 371 { 372 if (value.IsNull()) 373 { 374 return ToDuk(ctx, nullptr); 375 } 376 377 DukObject dukCoords(ctx); 378 dukCoords.Set("x", value.x); 379 dukCoords.Set("y", value.y); 380 dukCoords.Set("z", value.z); 381 dukCoords.Set("direction", value.direction); 382 return dukCoords.Take(); 383 } 384 ToDuk(duk_context * ctx,const GForces & value)385 template<> inline DukValue ToDuk(duk_context* ctx, const GForces& value) 386 { 387 DukObject dukGForces(ctx); 388 dukGForces.Set("lateralG", value.LateralG); 389 dukGForces.Set("verticalG", value.VerticalG); 390 return dukGForces.Take(); 391 } 392 FromDuk(const DukValue & value)393 template<> inline CoordsXYZD FromDuk(const DukValue& value) 394 { 395 CoordsXYZD result; 396 if (value.type() == DukValue::Type::OBJECT) 397 { 398 result.x = AsOrDefault(value["x"], 0); 399 result.y = AsOrDefault(value["y"], 0); 400 result.z = AsOrDefault(value["z"], 0); 401 result.direction = AsOrDefault(value["direction"], 0); 402 } 403 else 404 { 405 result.SetNull(); 406 } 407 return result; 408 } 409 ToDuk(duk_context * ctx,const ScreenSize & value)410 template<> inline DukValue ToDuk(duk_context* ctx, const ScreenSize& value) 411 { 412 DukObject dukCoords(ctx); 413 dukCoords.Set("width", value.width); 414 dukCoords.Set("height", value.height); 415 return dukCoords.Take(); 416 } 417 418 } // namespace OpenRCT2::Scripting 419 420 #endif 421