From a16cacff3a633bd5b8bae61891132a8235e20ced Mon Sep 17 00:00:00 2001 From: Ted John Date: Thu, 5 Mar 2020 18:30:41 +0000 Subject: [PATCH] Add special properties for ride action --- src/openrct2/actions/RideCreateAction.hpp | 10 ++ src/openrct2/scripting/Duktape.hpp | 134 +++++++++++++++++++++- src/openrct2/scripting/ScriptEngine.cpp | 54 ++++++--- src/thirdparty/dukglue/dukvalue.h | 6 +- 4 files changed, 178 insertions(+), 26 deletions(-) diff --git a/src/openrct2/actions/RideCreateAction.hpp b/src/openrct2/actions/RideCreateAction.hpp index cb85997acf..5b13f591e6 100644 --- a/src/openrct2/actions/RideCreateAction.hpp +++ b/src/openrct2/actions/RideCreateAction.hpp @@ -60,6 +60,16 @@ public: { } + int32_t GetRideType() const + { + return _rideType; + } + + int32_t GetRideObject() const + { + return _subType; + } + uint16_t GetActionFlags() const override { return GameAction::GetActionFlags() | GA_FLAGS::ALLOW_WHILE_PAUSED; diff --git a/src/openrct2/scripting/Duktape.hpp b/src/openrct2/scripting/Duktape.hpp index 996d7ac0b7..bead6995f2 100644 --- a/src/openrct2/scripting/Duktape.hpp +++ b/src/openrct2/scripting/Duktape.hpp @@ -11,8 +11,10 @@ #ifdef __ENABLE_SCRIPTING__ +# include # include # include +# include template DukValue GetObjectAsDukValue(duk_context* ctx, const std::shared_ptr& value) { @@ -20,19 +22,139 @@ template DukValue GetObjectAsDukValue(duk_context* ctx, const std::s return DukValue::take_from_stack(ctx); } -template -T AsOrDefault(const DukValue& value, const T& defaultValue = {}) = delete; +template T AsOrDefault(const DukValue& value, const T& defaultValue = {}) = delete; -template<> -inline std::string AsOrDefault(const DukValue& value, const std::string& defaultValue) +template<> inline std::string AsOrDefault(const DukValue& value, const std::string& defaultValue) { return value.type() == DukValue::STRING ? value.as_string() : defaultValue; } -template<> -inline int32_t AsOrDefault(const DukValue& value, const int32_t& defaultValue) +template<> inline int32_t AsOrDefault(const DukValue& value, const int32_t& defaultValue) { return value.type() == DukValue::NUMBER ? value.as_int() : defaultValue; } +/** + * Allows creation of an object on the duktape stack and setting properties on it before + * retrieving the DukValue instance of it. + */ +class DukObject +{ +private: + duk_context* _ctx{}; + duk_idx_t _idx = DUK_INVALID_INDEX; + +public: + DukObject(duk_context* ctx) + : _ctx(ctx) + { + } + + DukObject(const DukObject&) = delete; + + DukObject(DukObject&& m) noexcept + { + _ctx = m._ctx; + _idx = m._idx; + m._ctx = {}; + m._idx = {}; + } + + ~DukObject() + { + PopObjectIfExists(); + } + + void Set(const char* name, bool value) + { + EnsureObjectPushed(); + duk_push_boolean(_ctx, value); + duk_put_prop_string(_ctx, _idx, name); + } + + void Set(const char* name, int32_t value) + { + EnsureObjectPushed(); + duk_push_int(_ctx, value); + duk_put_prop_string(_ctx, _idx, name); + } + + void Set(const char* name, uint32_t value) + { + EnsureObjectPushed(); + duk_push_uint(_ctx, value); + duk_put_prop_string(_ctx, _idx, name); + } + + void Set(const char* name, const std::string_view& value) + { + EnsureObjectPushed(); + duk_push_lstring(_ctx, value.data(), value.size()); + duk_put_prop_string(_ctx, _idx, name); + } + + void Set(const char* name, const DukValue& value) + { + EnsureObjectPushed(); + value.push(); + duk_put_prop_string(_ctx, _idx, name); + } + + DukValue Take() + { + EnsureObjectPushed(); + auto result = DukValue::take_from_stack(_ctx, _idx); + _idx = DUK_INVALID_INDEX; + return result; + } + +private: + void PopObjectIfExists() + { + if (_idx != DUK_INVALID_INDEX) + { + duk_remove(_ctx, _idx); + _idx = DUK_INVALID_INDEX; + } + } + + void EnsureObjectPushed() + { + if (_idx == DUK_INVALID_INDEX) + { + _idx = duk_push_object(_ctx); + } + } +}; + +class DukStackFrame +{ +private: + duk_context* _ctx{}; + duk_idx_t _top; + +public: + DukStackFrame(duk_context* ctx) + : _ctx(ctx) + { + _top = duk_get_top(ctx); + } + + ~DukStackFrame() + { + auto top = duk_get_top(_ctx); + if (top != _top) + { + duk_set_top(_ctx, _top); + _ctx = {}; + std::fprintf(stderr, "duktape stack was not returned to original state!"); + // assert(false); + } + _ctx = {}; + } + + DukStackFrame(const DukStackFrame&) = delete; + DukStackFrame(DukStackFrame&&) = delete; +}; + #endif diff --git a/src/openrct2/scripting/ScriptEngine.cpp b/src/openrct2/scripting/ScriptEngine.cpp index 354c88c77b..2a2c09046f 100644 --- a/src/openrct2/scripting/ScriptEngine.cpp +++ b/src/openrct2/scripting/ScriptEngine.cpp @@ -13,6 +13,7 @@ # include "../PlatformEnvironment.h" # include "../actions/GameAction.h" +# include "../actions/RideCreateAction.hpp" # include "../config/Config.h" # include "../core/FileScanner.h" # include "../core/Path.hpp" @@ -658,6 +659,7 @@ std::future ScriptEngine::Eval(const std::string& s) DukValue ScriptEngine::ExecutePluginCall( const std::shared_ptr& plugin, const DukValue& func, const std::vector& args, bool isGameStateMutable) { + DukStackFrame frame(_context); if (func.is_function()) { ScriptExecutionInfo::PluginScope scope(_execInfo, plugin, isGameStateMutable); @@ -742,6 +744,7 @@ std::unique_ptr ScriptEngine::DukToGameActionResult(const DukV DukValue ScriptEngine::PositionToDuk(const CoordsXYZ& position) { + DukStackFrame frame(_context); duk_context* ctx = _context; auto obj = duk_push_object(ctx); duk_push_int(ctx, position.x); @@ -755,25 +758,33 @@ DukValue ScriptEngine::PositionToDuk(const CoordsXYZ& position) DukValue ScriptEngine::GameActionResultToDuk(const GameAction& action, const std::unique_ptr& result) { - duk_context* ctx = _context; - auto obj = duk_push_object(ctx); + DukStackFrame frame(_context); + DukObject obj(_context); + auto player = action.GetPlayer(); if (player != -1) { - duk_push_int(ctx, action.GetPlayer()); - duk_put_prop_string(ctx, obj, "player"); + obj.Set("player", action.GetPlayer()); } if (result->Cost != MONEY32_UNDEFINED) { - duk_push_int(ctx, result->Cost); - duk_put_prop_string(ctx, obj, "cost"); + obj.Set("cost", result->Cost); } if (!result->Position.isNull()) { - PositionToDuk(result->Position).push(); - duk_put_prop_string(ctx, obj, "position"); + obj.Set("position", PositionToDuk(result->Position)); } - return DukValue::take_from_stack(ctx); + + if (action.GetType() == GAME_COMMAND_CREATE_RIDE) + { + auto& rideCreateResult = static_cast(*result.get()); + if (rideCreateResult.rideIndex != RIDE_ID_NULL) + { + obj.Set("ride", rideCreateResult.rideIndex); + } + } + + return obj.Take(); } bool ScriptEngine::RegisterCustomAction( @@ -809,23 +820,30 @@ void ScriptEngine::RemoveCustomGameActions(const std::shared_ptr& plugin } } +# pragma warning(disable : 4189) void ScriptEngine::RunGameActionHooks(const GameAction& action, std::unique_ptr& result, bool isExecute) { + DukStackFrame frame(_context); + auto hookType = isExecute ? HOOK_TYPE::ACTION_EXECUTE : HOOK_TYPE::ACTION_QUERY; if (_hookEngine.HasSubscriptions(hookType)) { - duk_context* ctx = _context; - auto obj = duk_push_object(ctx); - duk_push_uint(ctx, action.GetType()); - duk_put_prop_string(ctx, obj, "type"); + DukObject obj(_context); + obj.Set("type", action.GetType()); auto flags = action.GetActionFlags(); - duk_push_boolean(ctx, (flags & GA_FLAGS::CLIENT_ONLY) != 0); - duk_put_prop_string(ctx, obj, "isClientOnly"); + obj.Set("isClientOnly", (flags & GA_FLAGS::CLIENT_ONLY) != 0); + + if (action.GetType() == GAME_COMMAND_CREATE_RIDE) + { + auto& rideCreateAction = static_cast(action); + obj.Set("rideType", rideCreateAction.GetRideType()); + obj.Set("rideObject", rideCreateAction.GetRideObject()); + } + + obj.Set("result", GameActionResultToDuk(action, result)); + auto dukEventArgs = obj.Take(); - GameActionResultToDuk(action, result).push(); - duk_put_prop_string(ctx, obj, "result"); - auto dukEventArgs = DukValue::take_from_stack(ctx); _hookEngine.Call(hookType, dukEventArgs, false); if (!isExecute) diff --git a/src/thirdparty/dukglue/dukvalue.h b/src/thirdparty/dukglue/dukvalue.h index 4e505cce05..59092244c5 100644 --- a/src/thirdparty/dukglue/dukvalue.h +++ b/src/thirdparty/dukglue/dukvalue.h @@ -593,7 +593,9 @@ public: { push(); duk_get_prop_string(mContext, -1, key.c_str()); - return DukValue::take_from_stack(mContext); + auto result = DukValue::take_from_stack(mContext); + duk_pop(mContext); + return result; } bool is_array() const @@ -633,4 +635,4 @@ public: duk_pop(mContext); return result; } -}; \ No newline at end of file +};